Una de les virtuts dels esports és que sempre hi ha la possibilitat que un rival teòricament inferior en guanyi al que suposadament és millor. Aquesta incapacitat d’endevinar un resultat és una de les claus que fan que els aficionats segueixin enganxats a les seves pantalles.
Tot i això, els humans tenim tendència a intentar predir el futur. Això passa en tots els camps, i el futbol no se n’escapa. Des de ben aviat, els seguidors fan juguesques per a demostrar qui és el que en sap més. Fins i tot, hi ha persones que es dediquen professionalment al camp de les apostes esportives.
Els avenços en els camps de l’estadística i l’aprenentatge automàtic han aportat una nova dimensió al món de les prediccions esportives. Ara, durant els partits s’emmagatzemen una gran quantitat de dades que posteriorment són utilitzats per analistes de dades per a desenvolupar models predictius.
En aquest treball, es proposa estudiar dades de futbol per entendre’l millor i per aconseguir un model que permeti predir tant el guanyador del partit com els gols marcats per cada equip.
Com ja s’ha comentat anteriorment, una de les tasques és fer un estudi de les dades per a identificar característiques importants (sobre els jugadors, els partits, etc.) que puguin determinar el resultat d’un partit.
Aquesta descripció és molt àmplia i, per tant, cal concretar-la en petites tasques. Ens centrarem principalment en els equips, els jugadors i els entrenadors. Es pot obtenir dades bàsiques, com quins són els que més partits guanyen, més gols marquen, targetes rebudes. Dades una mica més elaborades com intentar conèixer quins són els jugadors i combinacions de jugadors més influents dins del terreny de joc.
A més a més, també pot ser rellevant visualitzar aquestes dades tenint en compte el context d’aquests partits. És a dir, saber si hi ha diferència entre els equips que juguen de local o visitant, veure si l’hora influeix en el nombre de gols marcats, entendre si les diverses lligues i competicions determinen resultats diferents i observar si existeixen diferències entre cada temporada.
Aquesta feina de visualització permetrà conèixer molt millor les dades i facilitar la creació del model de machine learning. Com s’ha dit en l’anterior secció, aquest model ha de ser capaç de predir el guanyador d’un partit a partir d’unes dades d’entrada. Per tant, aquest seria un model de classificació.
Addicionalment, també es pot desenvolupar un model que sigui capaç de determinar la quantitat de gols d’un partit i la quantitat de gols marcats per cada equip. En conseqüència, aquest seria un model de regressió.
I finalment, es podria fer una anàlisi no supervisada per intentar agrupar els equips per característiques similars.
El dataset escollit per a dur a terme aquest treball és European Soccer Database. Aquest joc de dades es troba disponible de forma gratuita a la pàgina web Kaggle. Ha sigut creat especificament per a fer-la servir en projectes d’anàlisi de dades i aprenentatge automàtic.
Conté més de 25.000 partits, més de 10.000 jugadors, 11 lligues de països europeus, dades des de la temporada 2008/09 fins a la 2015/16, atributs dels jugadors i equips obtinguts del videojoc EA Sports FIFA, alineacions, quotes d’apostes i esdeveniments del partit detallats (tipus de gol, possessió, faltes, centrades, targetes, etc.) de més de 10.000 partits.
Tal com s’explica en la seva descripció, és probable que hi manquin alguns camps. Com per exemple, poden faltar alguns jugadors de les alienacions. Caldrà tenir això present quan es treballi amb el joc de dades.
El fitxer original és una base de dades SQLite.
Normalment, estem acostumats a treballar directament amb fitxers
.csv o .xlsx, però en el camp de la mineria de
dades també és comú treballar directament amb bases de dades.
En total hi ha 7 taules. A continuació, es mostra una descripció del contingut d’aquestes:
Conté informació sobre els països.
| Atribut | Descripció |
|---|---|
| id | Identificador del pais |
| name | Nom del pais |
Conté informació sobre les lligues disponibles.
| Atribut | Descripció |
|---|---|
| id | Identificador de la lliga |
| country_id | Identificador del pais |
| name | Nom de la lliga |
Conté informació sobre els partits disponibles.
| Atribut | Descripció |
|---|---|
| id | Identificador del partit |
| country_id | Identificador del pais |
| league_id | Identificador de la lliga |
| season | Temporada |
| stage | Jornada |
| date | Data |
| +team_api_id | Identificador de l’equip local o visitant a l’API |
| +team_goal | Nombre de gols de l’equip local o visitant |
| home_player+ | Onze titular de l’equip local |
| away_player+ | Onze titular de l’equip visitant |
| goal | Nombre de gols. Dades mal formatades |
| shoton | Nombre de tirs a porta. Dades mal formatades |
| shotoff | Nombre de tirs fora de porta. Dades mal formatades |
| foulcommit | Nombre de faltes. Dades mal formatades |
| card | Nombre de targetes. Dades mal formatades |
| cross | Nombre de centrades. Dades mal formatades |
| corner | Nombre de córners. Dades mal formatades |
| possession | Dades de possessió. Dades mal formatades |
| B365+ | Bet365 quotes de victòria/empat/derrota |
| BW+ | Bet&Win quotes de victòria/empat/derrota |
| IW+ | Interwetten quotes de victòria/empat/derrota |
| LB+ | Ladbrokes quotes de victòria/empat/derrota |
| PS+ | Pinnacle quotes de victòria/empat/derrota |
| WH+ | William Hill quotes de victòria/empat/derrota |
| SJ+ | Stan James quotes de victòria/empat/derrota |
| VC+ | VC Bet quotes de victòria/empat/derrota |
| GB+ | Game Bookers quotes de victòria/empat/derrota |
| BS+ | Blue Square quotes de victòria/empat/derrota |
El símbol suma ‘+’ de la taula s’ha d’interpretar com a múltiples possibles valors. Es representa d’aquesta forma per a facilitar la llegibilitat de la taula.
Conté informació descriptiva sobre alguns dels jugadors.
| Atribut | Descripció |
|---|---|
| id | Identificador del jugador |
| player_api_id | Identificador de l’API |
| player_name | Nom complet del jugador |
| player_fifa_api_id | Identificador de l’API del FIFA |
| birthday | Data d’aniversari |
| height | Alçada |
| weight | Pes |
Conté informació sobre els atributs dels jugadors al videojoc FIFA.
| Atribut | Descripció |
|---|---|
| id | Identificador del jugador |
| player_fifa_api_id | Identificador de l’API del FIFA |
| player_api_id | Identificador de l’API |
| player_name | Nom complet del jugador |
| date | Data |
| overall_rating | Qualificació global |
| potential | Potencial |
| preferred_foot | Peu preferit |
| +work_rate | Taxa d’atac/defensa |
| crossing | Qualificació de les centrades |
| finishing | Qualificació definint |
| heading_accuracy | Precisió amb rematades de cap |
| short_passing | Qualificació de les passades curtes |
| volleys | Qualificació de les volees |
| dribbling | Capacitat per regatejar |
| curve | Capacitat per xutar amb efecte |
| free_kick_accuracy | Precisió amb els tirs lliures |
| long_passing | Qualificació de les passades llargues |
| ball_control | Qualificació del control de pilota |
| acceleration | Qualificació de la capacitat d’accelerar |
| sprint_speed | Velocitat en l’esprint |
| agility | Agilitat del jugador |
| reactions | Capacitat de reaccionar |
| balance | Equilibri |
| shot_power | Potència de tir |
| jumping | Capacitat de saltar |
| stamina | Resistència |
| strength | Força |
| long_shots | Capacitat de fer tirs llunyans |
| aggression | Agresivitat |
| interceptions | Capacitat d’interceptar una pilota |
| positioning | Capacitat de situar-se correctament al camp |
| vision | Capacitat de veure els companys |
| penaltis | Qualificació llençant penaltis |
| marking | Capacitat de marcar a un oponent |
| standing_tackle | Capacitat de prendre la pilota dempeus |
| sliding_tacke | Capacitat de prendre la pilota llençant-se al terra |
| gk_diving | Qualificació de l’estirada d’un porter |
| gk_handing | Habilitat del porter amb les mans |
| gk_kicking | Habilitat de xutar d’un porter |
| gk_positioning | Habilitat d’un porter de col·locar-se correctament |
| gk_reflexer | Reflexes d’un porter |
Conté informació molt bàsica sobre els equips.
| Atribut | Descripció |
|---|---|
| id | Identificador de l’equip |
| team_api_id | Identificador de l’API |
| team_fifa_api_id | Identificador de l’API del FIFA |
| team_long_name | Nom llarg de l’equip |
| team_short_name | Abreviació del nom de l’equip |
Conté informació sobre estils de joc dels equips.
| Atribut | Descripció |
|---|---|
| id | Identificador de l’equip |
| team_fifa_api_id | Identificador de l’API del FIFA |
| team_api_id | Identificador de l’API |
| date | Data |
| buildUpPlaySpeed | Velocitat de construcció de joc |
| buildUpPlaySpeedClass | Tipus de velocitat de construcció de joc |
| buildUpPlayDribbling | Quantitat de regats en la construcció de joc |
| buildUpPlayDribblingClass | Categories de quantitat de regats |
| buildUpPlayPassing | Quantitat de passades en la construcció de joc |
| buildUpPlayPassingClass | Categories de quantitat de passades |
| buildUpPlayPositioningClass | Categories de posicionament en la construcció de joc |
| chanceCreationPassing | Quantitat de passades |
| chanceCreationPassingClass | Tipus de passades |
| chanceCreationCrossing | Oportunitats creades amb centrades |
| chanceCreationCrossingClass | Tipus de centrades |
| chanceCreationShooting | Quantitat de xuts |
| chanceCreationShootingClass | Tipus de xuts |
| chanceCreationPositioningClass | Tipus d’organització |
| defencePressure | Quantitat de pressió defensiva |
| defencePressureClass | Tipus de pressió defensiva |
| defenceAggression | Agressivitat defensiva |
| defenceAggressionClass | Tipus d’agressivitat defensiva |
| defenceTeamWidth | Amplada defensiva |
| defenceTeamWidthClass | Tipus d’amplada defensiva |
| defenceDefenderLineClass | Tipus de defensa |
S’ha escollit aquest joc de dades per què dels datasets disponibles sobre futbol, aquest és un dels més complets. Molts d’ells no contenen gaire informació dels equips, partits i jugadors, en canvi, aquest sí. És cert que no conté dades gaire actualitzades, però això no és rellevant.
A més, se li poden aplicar tant algorismes supervisats com no supervisats, conté més de 500 observacions, més de 5 variables numèriques, més de 2 categòriques i més d’1 binària.
Pel que fa a la seva complexitat es pot comentar que té múltiples taules i això farà que no sigui tan fàcil treballar amb ell. A més, les dades no estan gaire polides i hi ha poca documentació sobre el significat de les columnes, ha calgut fer recerca per esbrinar el significat d’algunes de les variables. I finalment, és un arxiu SQLite, i no estem massa acostumats a fer servir aquest tipus de base de dades.
En aquesta secció es fa una anàlisi de les dades per entendre-les. Cal fer una investigació dels registres i atributs per obtenir les seues principals característiques. Els objectius principals d’aquesta fase són extreure’n el coneixement suficient per a saber com manipular les fonts de dades en posteriors estudis.
Abans de res cal aconseguir les dades. Les tenim en un arxiu
sqlite, per tant, cal carregar-lo en una base de dades,
obtenir les seves taules i guardar-les com a Data
Frames per a poder-les fer servir en
R.
A continuació, es crida un script que s’encarrega de fer aquesta feina:
# Load the RSQLite Library
if (!require("RSQLite")) install.packages("RSQLite", repos = "http:/cran.us.r-project.org"); library("RSQLite")
# Create connection to the SQLite DB file
mydb <- dbConnect(RSQLite::SQLite(), "data/database.sqlite")
# Retrieve dataframes
df_country <- dbGetQuery(mydb, "SELECT * FROM Country")
df_league <- dbGetQuery(mydb, "SELECT * FROM League")
df_match <- dbGetQuery(mydb, "SELECT * FROM Match")
df_player <- dbGetQuery(mydb, "SELECT * FROM Player")
df_player_attributes <- dbGetQuery(mydb, "SELECT * FROM Player_Attributes")
df_team <- dbGetQuery(mydb, "SELECT * FROM Team")
df_team_attributes <- dbGetQuery(mydb, "SELECT * FROM Team_Attributes")
# Close connection with DB
dbDisconnect(mydb)
En aquesta secció es dona una primera ullada a les taules que acabem de carregar i analitzem la seua estructura.
Visualitzem les dades de les taules country i
league:
df_country
## id name
## 1 1 Belgium
## 2 1729 England
## 3 4769 France
## 4 7809 Germany
## 5 10257 Italy
## 6 13274 Netherlands
## 7 15722 Poland
## 8 17642 Portugal
## 9 19694 Scotland
## 10 21518 Spain
## 11 24558 Switzerland
df_league
## id country_id name
## 1 1 1 Belgium Jupiler League
## 2 1729 1729 England Premier League
## 3 4769 4769 France Ligue 1
## 4 7809 7809 Germany 1. Bundesliga
## 5 10257 10257 Italy Serie A
## 6 13274 13274 Netherlands Eredivisie
## 7 15722 15722 Poland Ekstraklasa
## 8 17642 17642 Portugal Liga ZON Sagres
## 9 19694 19694 Scotland Premier League
## 10 21518 21518 Spain LIGA BBVA
## 11 24558 24558 Switzerland Super League
Com que és una taula auxiliar no conté gaires registres. La seua única finalitat serà la de facilitar la visualització de la resta de taules.
Aquesta taula és una de les més importants de tot el conjunt de dades. Ens servirà per conèixer els resultats dels partits i les seues principals característiques.
Fem una visualització de la seva estructura:
str(df_match)
## 'data.frame': 25979 obs. of 115 variables:
## $ id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ country_id : int 1 1 1 1 1 1 1 1 1 1 ...
## $ league_id : int 1 1 1 1 1 1 1 1 1 1 ...
## $ season : chr "2008/2009" "2008/2009" "2008/2009" "2008/2009" ...
## $ stage : int 1 1 1 1 1 1 1 1 1 10 ...
## $ date : chr "2008-08-17 00:00:00" "2008-08-16 00:00:00" "2008-08-16 00:00:00" "2008-08-17 00:00:00" ...
## $ match_api_id : int 492473 492474 492475 492476 492477 492478 492479 492480 492481 492564 ...
## $ home_team_api_id: int 9987 10000 9984 9991 7947 8203 9999 4049 10001 8342 ...
## $ away_team_api_id: int 9993 9994 8635 9998 9985 8342 8571 9996 9986 8571 ...
## $ home_team_goal : int 1 0 0 5 1 1 2 1 1 4 ...
## $ away_team_goal : int 1 0 3 0 3 1 2 2 0 1 ...
## $ home_player_X1 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_X2 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_X3 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_X4 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_X5 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_X6 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_X7 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_X8 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_X9 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_X10 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_X11 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_X1 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_X2 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_X3 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_X4 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_X5 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_X6 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_X7 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_X8 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_X9 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_X10 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_X11 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_Y1 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_Y2 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_Y3 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_Y4 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_Y5 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_Y6 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_Y7 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_Y8 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_Y9 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_Y10 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_Y11 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_Y1 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_Y2 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_Y3 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_Y4 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_Y5 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_Y6 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_Y7 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_Y8 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_Y9 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_Y10 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_Y11 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_1 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_2 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_3 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_4 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_5 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_6 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_7 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_8 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_9 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_10 : int NA NA NA NA NA NA NA NA NA NA ...
## $ home_player_11 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_1 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_2 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_3 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_4 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_5 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_6 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_7 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_8 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_9 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_10 : int NA NA NA NA NA NA NA NA NA NA ...
## $ away_player_11 : int NA NA NA NA NA NA NA NA NA NA ...
## $ goal : chr NA NA NA NA ...
## $ shoton : chr NA NA NA NA ...
## $ shotoff : chr NA NA NA NA ...
## $ foulcommit : chr NA NA NA NA ...
## $ card : chr NA NA NA NA ...
## $ cross : chr NA NA NA NA ...
## $ corner : chr NA NA NA NA ...
## $ possession : chr NA NA NA NA ...
## $ B365H : num 1.73 1.95 2.38 1.44 5 4.75 2.1 3.2 2.25 1.3 ...
## $ B365D : num 3.4 3.2 3.3 3.75 3.5 3.4 3.2 3.4 3.25 5.25 ...
## $ B365A : num 5 3.6 2.75 7.5 1.65 1.67 3.3 2.2 2.88 9.5 ...
## $ BWH : num 1.75 1.8 2.4 1.4 5 4.85 2.05 2.55 2.3 1.25 ...
## $ BWD : num 3.35 3.3 3.3 4 3.5 3.4 3.25 3.3 3.25 5 ...
## $ BWA : num 4.2 3.95 2.55 6.8 1.6 1.65 3.15 2.4 2.7 10 ...
## $ IWH : num 1.85 1.9 2.6 1.4 4 3.7 1.85 2.4 2.1 1.3 ...
## $ IWD : num 3.2 3.2 3.1 3.9 3.3 3.2 3.2 3.2 3.1 4.2 ...
## $ IWA : num 3.5 3.5 2.3 6 1.7 1.8 3.5 2.4 3 8 ...
## $ LBH : num 1.8 1.9 2.5 1.44 4 5 1.83 2.5 2.25 1.25 ...
## $ LBD : num 3.3 3.2 3.2 3.6 3.4 3.25 3.3 3.2 3.2 4.5 ...
## $ LBA : num 3.75 3.5 2.5 6.5 1.72 1.62 3.6 2.5 2.75 10 ...
## $ PSH : num NA NA NA NA NA NA NA NA NA NA ...
## $ PSD : num NA NA NA NA NA NA NA NA NA NA ...
## [list output truncated]
Aquestes comandes ens serveixen per saber el tipus de cada variable i fer una primera inspecció al contingut de les variables. També es pot observar que hi ha variables amb valors buits.
Els valors buits (NA) es tractaran més endavant en la
secció de Neteja i preparació, però volem indagar una
mica més per conèixer quines són les variables a les quals els manquen
més valors:
sort(colMeans(is.na(df_match) | df_match == ""), decreasing = TRUE)
## PSH PSD PSA BSH
## 0.57011432 0.57011432 0.57011432 0.45490589
## BSD BSA GBH GBD
## 0.45490589 0.45490589 0.45486739 0.45486739
## GBA goal shoton shotoff
## 0.45486739 0.45275030 0.45275030 0.45275030
## foulcommit card cross corner
## 0.45275030 0.45275030 0.45275030 0.45275030
## possession SJH SJD SJA
## 0.45275030 0.34189153 0.34189153 0.34189153
## IWH IWD IWA LBH
## 0.13314600 0.13314600 0.13314600 0.13176027
## LBD LBA VCH VCD
## 0.13176027 0.13176027 0.13129836 0.13129836
## VCA WHH WHD WHA
## 0.13129836 0.13118288 0.13118288 0.13118288
## BWH BWD BWA B365H
## 0.13102891 0.13102891 0.13102891 0.13037453
## B365D B365A away_player_X11 away_player_Y11
## 0.13037453 0.13037453 0.07078794 0.07078794
## away_player_X9 away_player_X10 away_player_Y9 away_player_Y10
## 0.07055699 0.07055699 0.07055699 0.07055699
## home_player_X3 home_player_X4 home_player_X5 home_player_X6
## 0.07051850 0.07051850 0.07051850 0.07051850
## home_player_X7 home_player_X8 home_player_X9 home_player_X10
## 0.07051850 0.07051850 0.07051850 0.07051850
## home_player_X11 away_player_X1 away_player_X2 away_player_X3
## 0.07051850 0.07051850 0.07051850 0.07051850
## away_player_X4 away_player_X5 away_player_X6 away_player_X7
## 0.07051850 0.07051850 0.07051850 0.07051850
## away_player_X8 home_player_Y3 home_player_Y4 home_player_Y5
## 0.07051850 0.07051850 0.07051850 0.07051850
## home_player_Y6 home_player_Y7 home_player_Y8 home_player_Y9
## 0.07051850 0.07051850 0.07051850 0.07051850
## home_player_Y10 home_player_Y11 away_player_Y1 away_player_Y2
## 0.07051850 0.07051850 0.07051850 0.07051850
## away_player_Y3 away_player_Y4 away_player_Y5 away_player_Y6
## 0.07051850 0.07051850 0.07051850 0.07051850
## away_player_Y7 away_player_Y8 home_player_X1 home_player_X2
## 0.07051850 0.07051850 0.07009508 0.07009508
## home_player_Y1 home_player_Y2 home_player_11 away_player_11
## 0.07009508 0.07009508 0.05985604 0.05981754
## away_player_10 home_player_10 away_player_8 away_player_5
## 0.05546788 0.05527541 0.05161862 0.05138766
## away_player_9 home_player_6 home_player_4 away_player_4
## 0.05111821 0.05100273 0.05092575 0.05084876
## home_player_5 home_player_2 away_player_6 home_player_8
## 0.05065630 0.05061781 0.05054082 0.05038685
## away_player_3 home_player_3 away_player_2 home_player_9
## 0.04977097 0.04930906 0.04919358 0.04900112
## away_player_7 away_player_1 home_player_7 home_player_1
## 0.04753840 0.04749990 0.04723046 0.04711498
## id country_id league_id season
## 0.00000000 0.00000000 0.00000000 0.00000000
## stage date match_api_id home_team_api_id
## 0.00000000 0.00000000 0.00000000 0.00000000
## away_team_api_id home_team_goal away_team_goal
## 0.00000000 0.00000000 0.00000000
Com podem veure les columnes amb més valors buits són les que tenen
relació amb les quotes d’apostes. En principi, no ens preocupa perquè no
tenim cap propòsit de predir o entendre les quotes d’apostes. Tot i
això, si es canviés d’opinió, es podrien fer servir les columnes
B365+, BW+, WH+,
VC+, LB+ i IW+.
També es veu com les variables goal,
shoton, shotoff, foulcommit,
card, cross, corner i
possession contenen molts registres buits. A més, aquestes
hem vist que estan mal formatades. És una llàstima per què a partir
d’aquestes estadístiques dels partits se’n podrien obtenir conclusions
interessants. Malauradament, sembla que no les podrem fer servir.
Ara donem una primera ullada a les taules player i
team.
head(df_player)
## id player_api_id player_name player_fifa_api_id birthday
## 1 1 505942 Aaron Appindangoye 218353 1992-02-29 00:00:00
## 2 2 155782 Aaron Cresswell 189615 1989-12-15 00:00:00
## 3 3 162549 Aaron Doran 186170 1991-05-13 00:00:00
## 4 4 30572 Aaron Galindo 140161 1982-05-08 00:00:00
## 5 5 23780 Aaron Hughes 17725 1979-11-08 00:00:00
## 6 6 27316 Aaron Hunt 158138 1986-09-04 00:00:00
## height weight
## 1 182.88 187
## 2 170.18 146
## 3 170.18 163
## 4 182.88 198
## 5 182.88 154
## 6 182.88 161
str(df_player)
## 'data.frame': 11060 obs. of 7 variables:
## $ id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ player_api_id : int 505942 155782 162549 30572 23780 27316 564793 30895 528212 101042 ...
## $ player_name : chr "Aaron Appindangoye" "Aaron Cresswell" "Aaron Doran" "Aaron Galindo" ...
## $ player_fifa_api_id: int 218353 189615 186170 140161 17725 158138 221280 152747 206592 188621 ...
## $ birthday : chr "1992-02-29 00:00:00" "1989-12-15 00:00:00" "1991-05-13 00:00:00" "1982-05-08 00:00:00" ...
## $ height : num 183 170 170 183 183 ...
## $ weight : int 187 146 163 198 154 161 146 139 181 170 ...
head(df_team)
## id team_api_id team_fifa_api_id team_long_name team_short_name
## 1 1 9987 673 KRC Genk GEN
## 2 2 9993 675 Beerschot AC BAC
## 3 3 10000 15005 SV Zulte-Waregem ZUL
## 4 4 9994 2007 Sporting Lokeren LOK
## 5 5 9984 1750 KSV Cercle Brugge CEB
## 6 6 8635 229 RSC Anderlecht AND
str(df_team)
## 'data.frame': 299 obs. of 5 variables:
## $ id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ team_api_id : int 9987 9993 10000 9994 9984 8635 9991 9998 7947 9985 ...
## $ team_fifa_api_id: int 673 675 15005 2007 1750 229 674 1747 NA 232 ...
## $ team_long_name : chr "KRC Genk" "Beerschot AC" "SV Zulte-Waregem" "Sporting Lokeren" ...
## $ team_short_name : chr "GEN" "BAC" "ZUL" "LOK" ...
Com podem veure, aquestes taules tampoc conten gaire informació sobre els jugadors i els equips. Però es podran fer servir com a taules auxiliars.
Aquesta taula també és important, ja que, ens indica els principals atributs dels jugadors de futbol.
Fem una visualització de l’estructura de les dades:
str(df_player_attributes)
## 'data.frame': 183978 obs. of 42 variables:
## $ id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ player_fifa_api_id : int 218353 218353 218353 218353 218353 189615 189615 189615 189615 189615 ...
## $ player_api_id : int 505942 505942 505942 505942 505942 155782 155782 155782 155782 155782 ...
## $ date : chr "2016-02-18 00:00:00" "2015-11-19 00:00:00" "2015-09-21 00:00:00" "2015-03-20 00:00:00" ...
## $ overall_rating : int 67 67 62 61 61 74 74 73 73 73 ...
## $ potential : int 71 71 66 65 65 76 76 75 75 75 ...
## $ preferred_foot : chr "right" "right" "right" "right" ...
## $ attacking_work_rate: chr "medium" "medium" "medium" "medium" ...
## $ defensive_work_rate: chr "medium" "medium" "medium" "medium" ...
## $ crossing : int 49 49 49 48 48 80 80 79 79 79 ...
## $ finishing : int 44 44 44 43 43 53 53 52 51 51 ...
## $ heading_accuracy : int 71 71 71 70 70 58 58 57 57 57 ...
## $ short_passing : int 61 61 61 60 60 71 71 70 70 70 ...
## $ volleys : int 44 44 44 43 43 40 32 29 29 29 ...
## $ dribbling : int 51 51 51 50 50 73 73 71 71 71 ...
## $ curve : int 45 45 45 44 44 70 70 68 68 68 ...
## $ free_kick_accuracy : int 39 39 39 38 38 69 69 69 69 69 ...
## $ long_passing : int 64 64 64 63 63 68 68 68 68 68 ...
## $ ball_control : int 49 49 49 48 48 71 71 70 70 70 ...
## $ acceleration : int 60 60 60 60 60 79 79 79 79 79 ...
## $ sprint_speed : int 64 64 64 64 64 78 78 78 78 78 ...
## $ agility : int 59 59 59 59 59 78 78 78 78 78 ...
## $ reactions : int 47 47 47 46 46 67 67 67 67 67 ...
## $ balance : int 65 65 65 65 65 90 90 90 90 90 ...
## $ shot_power : int 55 55 55 54 54 71 71 71 71 71 ...
## $ jumping : int 58 58 58 58 58 85 85 84 84 84 ...
## $ stamina : int 54 54 54 54 54 79 79 79 79 79 ...
## $ strength : int 76 76 76 76 76 56 56 56 56 56 ...
## $ long_shots : int 35 35 35 34 34 62 60 59 58 58 ...
## $ aggression : int 71 71 63 62 62 68 68 67 67 67 ...
## $ interceptions : int 70 70 41 40 40 67 67 66 66 66 ...
## $ positioning : int 45 45 45 44 44 60 60 58 58 58 ...
## $ vision : int 54 54 54 53 53 66 66 65 65 65 ...
## $ penalties : int 48 48 48 47 47 59 59 59 59 59 ...
## $ marking : int 65 65 65 62 62 76 76 76 76 76 ...
## $ standing_tackle : int 69 69 66 63 63 75 75 75 75 75 ...
## $ sliding_tackle : int 69 69 69 66 66 78 78 78 78 78 ...
## $ gk_diving : int 6 6 6 5 5 14 14 14 14 14 ...
## $ gk_handling : int 11 11 11 10 10 7 7 7 7 7 ...
## $ gk_kicking : int 10 10 10 9 9 9 9 9 9 9 ...
## $ gk_positioning : int 8 8 8 7 7 9 9 9 9 9 ...
## $ gk_reflexes : int 8 8 8 7 7 12 12 12 12 12 ...
Comprovem també la quantitat de valors buits:
sort(colMeans(is.na(df_player_attributes) | df_player_attributes == ""), decreasing = TRUE)
## attacking_work_rate volleys curve agility
## 0.017556447 0.014746328 0.014746328 0.014746328
## balance jumping vision sliding_tackle
## 0.014746328 0.014746328 0.014746328 0.014746328
## overall_rating potential preferred_foot defensive_work_rate
## 0.004544022 0.004544022 0.004544022 0.004544022
## crossing finishing heading_accuracy short_passing
## 0.004544022 0.004544022 0.004544022 0.004544022
## dribbling free_kick_accuracy long_passing ball_control
## 0.004544022 0.004544022 0.004544022 0.004544022
## acceleration sprint_speed reactions shot_power
## 0.004544022 0.004544022 0.004544022 0.004544022
## stamina strength long_shots aggression
## 0.004544022 0.004544022 0.004544022 0.004544022
## interceptions positioning penalties marking
## 0.004544022 0.004544022 0.004544022 0.004544022
## standing_tackle gk_diving gk_handling gk_kicking
## 0.004544022 0.004544022 0.004544022 0.004544022
## gk_positioning gk_reflexes id player_fifa_api_id
## 0.004544022 0.004544022 0.000000000 0.000000000
## player_api_id date
## 0.000000000 0.000000000
Per sort, en aquest cas la gran majoria de registres tenen algun valor.
Finalment, fem l’anàlisi preliminar de la taula
team_attributes. També és rellevant per què ens indica les
principals característiques dels clubs.
Fem una visualització de l’estructura de les dades:
str(df_team_attributes)
## 'data.frame': 1458 obs. of 25 variables:
## $ id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ team_fifa_api_id : int 434 434 434 77 77 77 77 77 77 614 ...
## $ team_api_id : int 9930 9930 9930 8485 8485 8485 8485 8485 8485 8576 ...
## $ date : chr "2010-02-22 00:00:00" "2014-09-19 00:00:00" "2015-09-10 00:00:00" "2010-02-22 00:00:00" ...
## $ buildUpPlaySpeed : int 60 52 47 70 47 58 62 58 59 60 ...
## $ buildUpPlaySpeedClass : chr "Balanced" "Balanced" "Balanced" "Fast" ...
## $ buildUpPlayDribbling : int NA 48 41 NA NA NA NA 64 64 NA ...
## $ buildUpPlayDribblingClass : chr "Little" "Normal" "Normal" "Little" ...
## $ buildUpPlayPassing : int 50 56 54 70 52 62 45 62 53 40 ...
## $ buildUpPlayPassingClass : chr "Mixed" "Mixed" "Mixed" "Long" ...
## $ buildUpPlayPositioningClass : chr "Organised" "Organised" "Organised" "Organised" ...
## $ chanceCreationPassing : int 60 54 54 70 53 45 40 56 51 45 ...
## $ chanceCreationPassingClass : chr "Normal" "Normal" "Normal" "Risky" ...
## $ chanceCreationCrossing : int 65 63 63 70 48 70 50 68 72 35 ...
## $ chanceCreationCrossingClass : chr "Normal" "Normal" "Normal" "Lots" ...
## $ chanceCreationShooting : int 55 64 64 70 52 55 55 57 63 55 ...
## $ chanceCreationShootingClass : chr "Normal" "Normal" "Normal" "Lots" ...
## $ chanceCreationPositioningClass: chr "Organised" "Organised" "Organised" "Organised" ...
## $ defencePressure : int 50 47 47 60 47 40 42 41 49 30 ...
## $ defencePressureClass : chr "Medium" "Medium" "Medium" "Medium" ...
## $ defenceAggression : int 55 44 44 70 47 40 42 42 45 70 ...
## $ defenceAggressionClass : chr "Press" "Press" "Press" "Double" ...
## $ defenceTeamWidth : int 45 54 54 70 52 60 60 60 63 30 ...
## $ defenceTeamWidthClass : chr "Normal" "Normal" "Normal" "Wide" ...
## $ defenceDefenderLineClass : chr "Cover" "Cover" "Cover" "Cover" ...
Veiem que conté molts atributs, però alguns estan duplicats. Algunes variables són valors numèrics i les altres variables categòriques. És molt probable que en la majoria de casos ja en tinguem prou fent servir una de les dos.
Comprovem també la quantitat de valors buits:
sort(colMeans(is.na(df_team_attributes) | df_team_attributes == ""), decreasing = TRUE)
## buildUpPlayDribbling id
## 0.6646091 0.0000000
## team_fifa_api_id team_api_id
## 0.0000000 0.0000000
## date buildUpPlaySpeed
## 0.0000000 0.0000000
## buildUpPlaySpeedClass buildUpPlayDribblingClass
## 0.0000000 0.0000000
## buildUpPlayPassing buildUpPlayPassingClass
## 0.0000000 0.0000000
## buildUpPlayPositioningClass chanceCreationPassing
## 0.0000000 0.0000000
## chanceCreationPassingClass chanceCreationCrossing
## 0.0000000 0.0000000
## chanceCreationCrossingClass chanceCreationShooting
## 0.0000000 0.0000000
## chanceCreationShootingClass chanceCreationPositioningClass
## 0.0000000 0.0000000
## defencePressure defencePressureClass
## 0.0000000 0.0000000
## defenceAggression defenceAggressionClass
## 0.0000000 0.0000000
## defenceTeamWidth defenceTeamWidthClass
## 0.0000000 0.0000000
## defenceDefenderLineClass
## 0.0000000
Podem observar que en aquesta taula també tenim un percentatge molt
petit de registres buits. Només la variable
buildUpPlayDribbling conté valors NA, però la
seva variable categòrica buildUpPlayDribblingClass és
plena.
En aquest apartat s’analitzen les variables de les taules una per una. Es vol obtenir estadístiques bàsiques sobre elles i visualitzar-les per a entendre-les més fàcilment.
En aquest cas, només fem servir les taules Match,
Player, Player_Attributes i
Team_Attributes. Tenen atributs numèrics o categòrics que
permeten analitzar-los en profunditat. En canvi, les taules
Country, League i Team només
contenen noms i identificadors, per la qual cosa no té sentit
examinar-ne el contingut.
Primer de tot, s’aconsegueixen unes estadístiques bàsiques de les
variables de cada taula. S’utilitza la funció summary(...),
aquesta mostra un resum de cada variable de la taula que se li passa per
paràmetre. Si la variable és numèrica mostra les següents
característiques:
Min.: Valor mínim de la sèrie de dades.1st Qu.: Valor del primer quartil de la sèrie de
dades.Median: Valor de la mediana de la sèrie de dades.Mean: Valor de la mitja de la sèrie de dades.3rd Qu.: Valor del tercer quartil de la sèrie de
dades.Max.: Valor màxim de la sèrie de dades.Cal tenir en compte que si un registre conté valors buits
(NA) la funció no els té en compte per a fer els càlculs,
però en mostra el nombre total.
Després, es generen uns gràfics que faciliten la comprensió de les dades. Per a les variables numèriques es fa servir el gràfic Box Plot per a veure com les dades estan distribuïdes i si contenen valors extrems (outliers). Per a les variables categòriques es creen histogrames, que mostren el nombre de registres que hi ha per cada categoria de la variable. També es fa servir un Pie Chart per a la variable binària.
summary(df_match)
## id country_id league_id season
## Min. : 1 Min. : 1 Min. : 1 Length:25979
## 1st Qu.: 6496 1st Qu.: 4769 1st Qu.: 4769 Class :character
## Median :12990 Median :10257 Median :10257 Mode :character
## Mean :12990 Mean :11739 Mean :11739
## 3rd Qu.:19484 3rd Qu.:17642 3rd Qu.:17642
## Max. :25979 Max. :24558 Max. :24558
##
## stage date match_api_id home_team_api_id
## Min. : 1.00 Length:25979 Min. : 483129 Min. : 1601
## 1st Qu.: 9.00 Class :character 1st Qu.: 768436 1st Qu.: 8475
## Median :18.00 Mode :character Median :1147511 Median : 8697
## Mean :18.24 Mean :1195429 Mean : 9984
## 3rd Qu.:27.00 3rd Qu.:1709852 3rd Qu.: 9925
## Max. :38.00 Max. :2216672 Max. :274581
##
## away_team_api_id home_team_goal away_team_goal home_player_X1
## Min. : 1601 Min. : 0.000 Min. :0.000 Min. :0.0000
## 1st Qu.: 8475 1st Qu.: 1.000 1st Qu.:0.000 1st Qu.:1.0000
## Median : 8697 Median : 1.000 Median :1.000 Median :1.0000
## Mean : 9984 Mean : 1.545 Mean :1.161 Mean :0.9996
## 3rd Qu.: 9925 3rd Qu.: 2.000 3rd Qu.:2.000 3rd Qu.:1.0000
## Max. :274581 Max. :10.000 Max. :9.000 Max. :2.0000
## NA's :1821
## home_player_X2 home_player_X3 home_player_X4 home_player_X5
## Min. :0.000 Min. :1.000 Min. :2.000 Min. :1.000
## 1st Qu.:2.000 1st Qu.:4.000 1st Qu.:6.000 1st Qu.:8.000
## Median :2.000 Median :4.000 Median :6.000 Median :8.000
## Mean :2.074 Mean :4.061 Mean :6.049 Mean :7.545
## 3rd Qu.:2.000 3rd Qu.:4.000 3rd Qu.:6.000 3rd Qu.:8.000
## Max. :8.000 Max. :8.000 Max. :8.000 Max. :9.000
## NA's :1821 NA's :1832 NA's :1832 NA's :1832
## home_player_X6 home_player_X7 home_player_X8 home_player_X9 home_player_X10
## Min. :1.000 Min. :1.00 Min. :1.00 Min. :1.000 Min. :1.000
## 1st Qu.:2.000 1st Qu.:4.00 1st Qu.:3.00 1st Qu.:5.000 1st Qu.:4.000
## Median :3.000 Median :5.00 Median :6.00 Median :5.000 Median :5.000
## Mean :3.185 Mean :4.77 Mean :5.31 Mean :5.822 Mean :5.389
## 3rd Qu.:4.000 3rd Qu.:6.00 3rd Qu.:7.00 3rd Qu.:8.000 3rd Qu.:7.000
## Max. :9.000 Max. :9.00 Max. :9.00 Max. :9.000 Max. :9.000
## NA's :1832 NA's :1832 NA's :1832 NA's :1832 NA's :1832
## home_player_X11 away_player_X1 away_player_X2 away_player_X3 away_player_X4
## Min. :1.000 Min. :1 Min. :1.000 Min. :2.000 Min. :1.000
## 1st Qu.:5.000 1st Qu.:1 1st Qu.:2.000 1st Qu.:4.000 1st Qu.:6.000
## Median :6.000 Median :1 Median :2.000 Median :4.000 Median :6.000
## Mean :5.783 Mean :1 Mean :2.075 Mean :4.059 Mean :6.052
## 3rd Qu.:6.000 3rd Qu.:1 3rd Qu.:2.000 3rd Qu.:4.000 3rd Qu.:6.000
## Max. :7.000 Max. :6 Max. :8.000 Max. :9.000 Max. :8.000
## NA's :1832 NA's :1832 NA's :1832 NA's :1832 NA's :1832
## away_player_X5 away_player_X6 away_player_X7 away_player_X8
## Min. :1.000 Min. :1.000 Min. :1.000 Min. :1.000
## 1st Qu.:8.000 1st Qu.:2.000 1st Qu.:4.000 1st Qu.:3.000
## Median :8.000 Median :3.000 Median :5.000 Median :6.000
## Mean :7.526 Mean :3.195 Mean :4.743 Mean :5.294
## 3rd Qu.:8.000 3rd Qu.:4.000 3rd Qu.:6.000 3rd Qu.:7.000
## Max. :9.000 Max. :9.000 Max. :9.000 Max. :9.000
## NA's :1832 NA's :1832 NA's :1832 NA's :1832
## away_player_X9 away_player_X10 away_player_X11 home_player_Y1
## Min. :1.000 Min. :1.000 Min. :3.000 Min. :0.0000
## 1st Qu.:5.000 1st Qu.:4.000 1st Qu.:5.000 1st Qu.:1.0000
## Median :5.000 Median :5.000 Median :6.000 Median :1.0000
## Mean :5.808 Mean :5.476 Mean :5.766 Mean :0.9996
## 3rd Qu.:8.000 3rd Qu.:7.000 3rd Qu.:6.000 3rd Qu.:1.0000
## Max. :9.000 Max. :9.000 Max. :8.000 Max. :3.0000
## NA's :1833 NA's :1833 NA's :1839 NA's :1821
## home_player_Y2 home_player_Y3 home_player_Y4 home_player_Y5 home_player_Y6
## Min. :0.000 Min. :3 Min. :3 Min. :3.000 Min. :3.000
## 1st Qu.:3.000 1st Qu.:3 1st Qu.:3 1st Qu.:3.000 1st Qu.:6.000
## Median :3.000 Median :3 Median :3 Median :3.000 Median :7.000
## Mean :2.999 Mean :3 Mean :3 Mean :3.237 Mean :6.477
## 3rd Qu.:3.000 3rd Qu.:3 3rd Qu.:3 3rd Qu.:3.000 3rd Qu.:7.000
## Max. :3.000 Max. :5 Max. :5 Max. :8.000 Max. :9.000
## NA's :1821 NA's :1832 NA's :1832 NA's :1832 NA's :1832
## home_player_Y7 home_player_Y8 home_player_Y9 home_player_Y10
## Min. :3.000 Min. : 3.000 Min. : 1.000 Min. : 3.000
## 1st Qu.:6.000 1st Qu.: 7.000 1st Qu.: 7.000 1st Qu.: 8.000
## Median :7.000 Median : 7.000 Median : 8.000 Median :10.000
## Mean :6.672 Mean : 7.239 Mean : 8.026 Mean : 9.219
## 3rd Qu.:7.000 3rd Qu.: 8.000 3rd Qu.: 8.000 3rd Qu.:10.000
## Max. :9.000 Max. :10.000 Max. :10.000 Max. :11.000
## NA's :1832 NA's :1832 NA's :1832 NA's :1832
## home_player_Y11 away_player_Y1 away_player_Y2 away_player_Y3 away_player_Y4
## Min. : 1.00 Min. :1 Min. :3 Min. :3 Min. :3
## 1st Qu.:10.00 1st Qu.:1 1st Qu.:3 1st Qu.:3 1st Qu.:3
## Median :10.00 Median :1 Median :3 Median :3 Median :3
## Mean :10.44 Mean :1 Mean :3 Mean :3 Mean :3
## 3rd Qu.:11.00 3rd Qu.:1 3rd Qu.:3 3rd Qu.:3 3rd Qu.:3
## Max. :11.00 Max. :3 Max. :3 Max. :7 Max. :7
## NA's :1832 NA's :1832 NA's :1832 NA's :1832 NA's :1832
## away_player_Y5 away_player_Y6 away_player_Y7 away_player_Y8
## Min. :3.000 Min. : 3.00 Min. : 3.00 Min. : 3.000
## 1st Qu.:3.000 1st Qu.: 6.00 1st Qu.: 6.00 1st Qu.: 7.000
## Median :3.000 Median : 7.00 Median : 7.00 Median : 7.000
## Mean :3.245 Mean : 6.47 Mean : 6.68 Mean : 7.246
## 3rd Qu.:3.000 3rd Qu.: 7.00 3rd Qu.: 7.00 3rd Qu.: 8.000
## Max. :9.000 Max. :10.00 Max. :10.00 Max. :10.000
## NA's :1832 NA's :1832 NA's :1832 NA's :1832
## away_player_Y9 away_player_Y10 away_player_Y11 home_player_1
## Min. : 5.000 Min. : 6.000 Min. : 7.00 Min. : 2984
## 1st Qu.: 7.000 1st Qu.: 8.000 1st Qu.:10.00 1st Qu.: 30602
## Median : 8.000 Median :10.000 Median :10.00 Median : 38230
## Mean : 8.022 Mean : 9.161 Mean :10.46 Mean : 76638
## 3rd Qu.: 8.000 3rd Qu.:10.000 3rd Qu.:11.00 3rd Qu.: 96836
## Max. :11.000 Max. :11.000 Max. :11.00 Max. :698273
## NA's :1833 NA's :1833 NA's :1839 NA's :1224
## home_player_2 home_player_3 home_player_4 home_player_5
## Min. : 2802 Min. : 2752 Min. : 2752 Min. : 2752
## 1st Qu.: 32574 1st Qu.: 30602 1st Qu.: 30627 1st Qu.: 33579
## Median : 42388 Median : 39731 Median : 41060 Median : 45996
## Mean :106854 Mean : 91601 Mean : 94540 Mean :109528
## 3rd Qu.:159854 3rd Qu.:128037 3rd Qu.:145561 3rd Qu.:160243
## Max. :748432 Max. :705484 Max. :723037 Max. :733787
## NA's :1315 NA's :1281 NA's :1323 NA's :1316
## home_player_6 home_player_7 home_player_8 home_player_9
## Min. : 2625 Min. : 2625 Min. : 2625 Min. : 2625
## 1st Qu.: 31037 1st Qu.: 30895 1st Qu.: 32751 1st Qu.: 33332
## Median : 41467 Median : 41432 Median : 43319 Median : 45605
## Mean :102309 Mean : 97288 Mean :107291 Mean :111132
## 3rd Qu.:150944 3rd Qu.:141699 3rd Qu.:160243 3rd Qu.:164479
## Max. :750584 Max. :692984 Max. :693171 Max. :730065
## NA's :1325 NA's :1227 NA's :1309 NA's :1273
## home_player_10 home_player_11 away_player_1 away_player_2
## Min. : 2625 Min. : 2802 Min. : 2796 Min. : 2790
## 1st Qu.: 32465 1st Qu.: 32627 1st Qu.: 30622 1st Qu.: 32579
## Median : 43296 Median : 42091 Median : 38289 Median : 42388
## Mean :105612 Mean :103414 Mean : 76628 Mean :107615
## 3rd Qu.:158783 3rd Qu.:161291 3rd Qu.: 96836 3rd Qu.:159882
## Max. :742405 Max. :726956 Max. :698273 Max. :748432
## NA's :1436 NA's :1555 NA's :1234 NA's :1278
## away_player_3 away_player_4 away_player_5 away_player_6
## Min. : 2752 Min. : 2752 Min. : 2790 Min. : 2625
## 1st Qu.: 30464 1st Qu.: 30627 1st Qu.: 33454 1st Qu.: 31037
## Median : 39892 Median : 41083 Median : 46212 Median : 41634
## Mean : 91127 Mean : 95084 Mean :109801 Mean :102308
## 3rd Qu.:121080 3rd Qu.:145561 3rd Qu.:160844 3rd Qu.:151079
## Max. :705484 Max. :728414 Max. :746419 Max. :722766
## NA's :1293 NA's :1321 NA's :1335 NA's :1313
## away_player_7 away_player_8 away_player_9 away_player_10
## Min. : 2625 Min. : 2625 Min. : 2625 Min. : 2770
## 1st Qu.: 30920 1st Qu.: 32863 1st Qu.: 33435 1st Qu.: 32627
## Median : 41433 Median : 45816 Median : 45860 Median : 45358
## Mean : 97898 Mean :109265 Mean :111087 Mean :107149
## 3rd Qu.:144996 3rd Qu.:163612 3rd Qu.:164209 3rd Qu.:161291
## Max. :750435 Max. :717248 Max. :722766 Max. :722766
## NA's :1235 NA's :1341 NA's :1328 NA's :1441
## away_player_11 goal shoton shotoff
## Min. : 2802 Length:25979 Length:25979 Length:25979
## 1st Qu.: 32747 Class :character Class :character Class :character
## Median : 42652 Mode :character Mode :character Mode :character
## Mean :104933
## 3rd Qu.:161660
## Max. :726956
## NA's :1554
## foulcommit card cross corner
## Length:25979 Length:25979 Length:25979 Length:25979
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## possession B365H B365D B365A
## Length:25979 Min. : 1.040 Min. : 1.40 Min. : 1.080
## Class :character 1st Qu.: 1.670 1st Qu.: 3.30 1st Qu.: 2.500
## Mode :character Median : 2.100 Median : 3.50 Median : 3.500
## Mean : 2.629 Mean : 3.84 Mean : 4.662
## 3rd Qu.: 2.800 3rd Qu.: 4.00 3rd Qu.: 5.250
## Max. :26.000 Max. :17.00 Max. :51.000
## NA's :3387 NA's :3387 NA's :3387
## BWH BWD BWA IWH
## Min. : 1.030 Min. : 1.650 Min. : 1.100 Min. : 1.030
## 1st Qu.: 1.650 1st Qu.: 3.200 1st Qu.: 2.500 1st Qu.: 1.650
## Median : 2.100 Median : 3.400 Median : 3.400 Median : 2.100
## Mean : 2.559 Mean : 3.748 Mean : 4.397 Mean : 2.468
## 3rd Qu.: 2.750 3rd Qu.: 3.800 3rd Qu.: 5.000 3rd Qu.: 2.600
## Max. :34.000 Max. :19.500 Max. :51.000 Max. :20.000
## NA's :3404 NA's :3404 NA's :3404 NA's :3459
## IWD IWA LBH LBD
## Min. : 1.500 Min. : 1.100 Min. : 1.040 Min. : 1.400
## 1st Qu.: 3.200 1st Qu.: 2.500 1st Qu.: 1.670 1st Qu.: 3.200
## Median : 3.300 Median : 3.300 Median : 2.100 Median : 3.400
## Mean : 3.609 Mean : 4.151 Mean : 2.536 Mean : 3.712
## 3rd Qu.: 3.700 3rd Qu.: 4.600 3rd Qu.: 2.700 3rd Qu.: 3.750
## Max. :11.000 Max. :25.000 Max. :26.000 Max. :19.000
## NA's :3459 NA's :3459 NA's :3423 NA's :3423
## LBA PSH PSD PSA
## Min. : 1.100 Min. : 1.040 Min. : 2.200 Min. : 1.090
## 1st Qu.: 2.500 1st Qu.: 1.720 1st Qu.: 3.410 1st Qu.: 2.560
## Median : 3.300 Median : 2.200 Median : 3.640 Median : 3.610
## Mean : 4.385 Mean : 2.816 Mean : 4.132 Mean : 4.973
## 3rd Qu.: 5.000 3rd Qu.: 2.980 3rd Qu.: 4.230 3rd Qu.: 5.410
## Max. :51.000 Max. :36.000 Max. :29.000 Max. :47.500
## NA's :3423 NA's :14811 NA's :14811 NA's :14811
## WHH WHD WHA SJH
## Min. : 1.020 Min. : 1.020 Min. : 1.080 Min. : 1.040
## 1st Qu.: 1.670 1st Qu.: 3.200 1st Qu.: 2.500 1st Qu.: 1.670
## Median : 2.150 Median : 3.300 Median : 3.400 Median : 2.100
## Mean : 2.579 Mean : 3.665 Mean : 4.483 Mean : 2.566
## 3rd Qu.: 2.750 3rd Qu.: 3.750 3rd Qu.: 5.000 3rd Qu.: 2.750
## Max. :26.000 Max. :17.000 Max. :51.000 Max. :23.000
## NA's :3408 NA's :3408 NA's :3408 NA's :8882
## SJD SJA VCH VCD
## Min. : 1.400 Min. : 1.100 Min. : 1.030 Min. : 1.620
## 1st Qu.: 3.250 1st Qu.: 2.500 1st Qu.: 1.700 1st Qu.: 3.300
## Median : 3.400 Median : 3.500 Median : 2.150 Median : 3.500
## Mean : 3.756 Mean : 4.622 Mean : 2.668 Mean : 3.899
## 3rd Qu.: 3.800 3rd Qu.: 5.250 3rd Qu.: 2.800 3rd Qu.: 4.000
## Max. :15.000 Max. :41.000 Max. :36.000 Max. :26.000
## NA's :8882 NA's :8882 NA's :3411 NA's :3411
## VCA GBH GBD GBA
## Min. : 1.08 Min. : 1.050 Min. : 1.450 Min. : 1.120
## 1st Qu.: 2.55 1st Qu.: 1.670 1st Qu.: 3.200 1st Qu.: 2.500
## Median : 3.50 Median : 2.100 Median : 3.300 Median : 3.400
## Mean : 4.84 Mean : 2.499 Mean : 3.648 Mean : 4.353
## 3rd Qu.: 5.40 3rd Qu.: 2.650 3rd Qu.: 3.750 3rd Qu.: 5.000
## Max. :67.00 Max. :21.000 Max. :11.000 Max. :34.000
## NA's :3411 NA's :11817 NA's :11817 NA's :11817
## BSH BSD BSA
## Min. : 1.040 Min. : 1.330 Min. : 1.120
## 1st Qu.: 1.670 1st Qu.: 3.250 1st Qu.: 2.500
## Median : 2.100 Median : 3.400 Median : 3.400
## Mean : 2.498 Mean : 3.661 Mean : 4.406
## 3rd Qu.: 2.620 3rd Qu.: 3.750 3rd Qu.: 5.000
## Max. :17.000 Max. :13.000 Max. :34.000
## NA's :11818 NA's :11818 NA's :11818
Aquesta taula té múltiples variables numèriques, però moltes d’elles només contenen identificadors, per tant, no té sentit estudiar la seua distribució.
També hi ha moltes variables relatives a les apostes. Com ja s’ha comentat anteriorment, no es pretén utilitzar-les en cap anàlisi i per això no se’n visualitza el seu Box Plot.
Les variables que resten són home_team_goal i
away_team_goal. Obtenim el seu Box Plot, ja que,
segurament seran molt importants de cara al nostre estudi.
box_plot_attr <- c("home_team_goal",
"away_team_goal")
df_match_aux <- df_match[, which(names(df_match) %in% box_plot_attr)]
boxplot(df_match_aux)
Podem veure que la mediana de totes dos columnes és molt semblant i està pel voltant dels 1,5 gols. Malgrat ser semblants, els gols que marca l’equip visitant no estan tan concentrats i el seu primer quartil es troba a 0, mentre que els gols que marca l’equip local tenen el primer quartil a 1,5 gols i estan molt més concentrats. A causa d’això, també apareixen més valors atípics.
Ara ha arribat el moment de mostrar la distribució de les variables
season i stage amb un histograma. És cert que
la variable stage és numèrica i es podria mostrar amb una
Box Plot. Però creiem més oportú visualitzar-ho així per què,
en realitat, aquests números són els identificadors de cada jornada i
les jornades d’una competició són variables categòriques.
Carreguem les llibreries necessàries per a Generació del els gràfics:
if (!require("ggplot2")) install.packages("ggplot2"); library("ggplot2")
if (!require("Rmisc")) install.packages("Rmisc"); library("Rmisc")
if (!require("dplyr")) install.packages("dplyr"); library("dplyr")
if (!require("xfun")) install.packages("xfun"); library("xfun")
Generem l’histograma de la columna season:
ggplot(df_match, aes_string(x = "season")) +
geom_histogram(stat = "count")
En el gràfic es veu que més o menys cada temporada conté el mateix nombre de registres, l’única que en té relativament pocs és la temporada 2013/2014. En principi, va ser una temporada normal en què es van jugar els mateixos partits que en la resta, així que deduïm que es deu a un error a l’hora de recollir les dades. No hauria de suposar un problema de cara a fer les nostres anàlisis, però caldrà tenir-ho en compte.
Generem l’histograma de la columna stage:
ggplot(df_match, aes_string(x = "stage")) +
geom_histogram(stat = "count")
Veiem com el nombre de registres per jornada disminueix cap al final de la competició. En certs casos és normal que hi hagi menys partits en algunes jornades perquè en les diverses lligues que tenim, no totes tenen el mateix nombre de jornades.
Hem fet una recerca per corroborar si la manca de registres es deu exclusivament a aquest factor:
Totes les lligues tenen almenys 34 jornades, per tant, és estrany que hi hagi menys partits entre les jornades 31 i 34. Suposem que és un error en recollir les dades.
summary(df_player)
## id player_api_id player_name player_fifa_api_id
## Min. : 1 Min. : 2625 Length:11060 Min. : 2
## 1st Qu.: 2768 1st Qu.: 35556 Class :character 1st Qu.:151890
## Median : 5536 Median : 96620 Mode :character Median :184671
## Mean : 5538 Mean :156582 Mean :165665
## 3rd Qu.: 8306 3rd Qu.:212470 3rd Qu.:203883
## Max. :11075 Max. :750584 Max. :234141
## birthday height weight
## Length:11060 Min. :157.5 Min. :117.0
## Class :character 1st Qu.:177.8 1st Qu.:159.0
## Mode :character Median :182.9 Median :168.0
## Mean :181.9 Mean :168.4
## 3rd Qu.:185.4 3rd Qu.:179.0
## Max. :208.3 Max. :243.0
Match, la taula
Player té poques variables numèriques per a
visualitzar.Tot i això, podem fer les gràfiques Box Plot de les
variables height i weigth:
box_plot_attr <- c("height",
"weight")
df_player_aux <- df_player[, which(names(df_player) %in% box_plot_attr)]
boxplot(df_player_aux)
El primer que podem veure és que la variable height està
expressada en centímetres i la variable weight està
expressada en pounds. Ens podem plantejar convertir el pes a
quilograms per entendre-ho millor.
Això vol dir que l’alçada mitjana els jugadors és de 181 cm i que la mitjana de pes és de 168 pounds, que equival a 76 kg. Veiem que l’alçada està molt concentrada i, en canvi, el pes presenta molta variància.
Aquesta taula també ens dona informació sobre la data de naixement dels jugadors. En principi, no és gaire interessant conèixer la distribució dels dies en què han nascut els jugadors. Però podem veure en quins anys o mesos han nascut més jugadors.
df_player$birth_year <- format(as.Date(df_player$birthday, format = "%Y-%m-%d"), "%Y")
ggplot(df_player, aes_string(x = "birth_year")) +
geom_histogram(stat = "count")
Podem veure que segueix una distribució normal lleugerament desplaçada cap a la dreta. L’any en què han nascut més jugadors és el 1988. Aquest joc de dades es va fer l’any 2016, això vol dir que l’edat més repetida era 28 anys. També veiem que els jugadors més joves tenen 17 anys i els més vells en tenen 49. Aquesta diferència d’edat és molt alta, però totalment normal.
Ara creem una nova variable birth_month amb el mes de
naixement de cada jugador, i en mostrem el seu histograma:
df_player$birth_month <- format(as.Date(df_player$birthday, format = "%Y-%m-%d"), "%m")
ggplot(df_player, aes_string(x = "birth_month")) +
geom_histogram(stat = "count")
Es pot veure com clarament hi ha menys jugadors a mesura que més tard neixen. Això sembla lògic, per què els que han nascut a principis d’any tenen més oportunitats de seguir progressant, ja que, s’enfronten a nens més joves.
Fem el mateix amb una nova variable birth_day que conté
el dia en què han nascut:
df_player$birth_day <- format(as.Date(df_player$birthday, format = "%Y-%m-%d"), "%d")
ggplot(df_player, aes_string(x = "birth_day")) +
geom_histogram(stat = "count")
Podem veure com el dia 1 és quan neixen més futbolistes i com el 31 és el dia que en neixen menys. Pel que fa a la resta de dies hi ha alguna diferència, però no és remarcable. Ens podem imaginar que el dia 31 és el que menys dies neixen per què n’hi ha menys durant l’any. Però no sabem per què el dia 1 és el més repetit.
Aquesta taula té moltes variables interessants per estudiar. La majoria d’elles són numèriques, i les visualitzem amb gràfiques Box Plot. Com que n’hi ha moltes, les agrupem per similitud. Les variables categòriques les mostrem amb un histograma i la variable binària s’estudia amb una Pie Chart.
summary(df_player_attributes)
## id player_fifa_api_id player_api_id date
## Min. : 1 Min. : 2 Min. : 2625 Length:183978
## 1st Qu.: 45995 1st Qu.:155798 1st Qu.: 34763 Class :character
## Median : 91990 Median :183488 Median : 77741 Mode :character
## Mean : 91990 Mean :165672 Mean :135901
## 3rd Qu.:137984 3rd Qu.:199848 3rd Qu.:191080
## Max. :183978 Max. :234141 Max. :750584
##
## overall_rating potential preferred_foot attacking_work_rate
## Min. :33.0 Min. :39.00 Length:183978 Length:183978
## 1st Qu.:64.0 1st Qu.:69.00 Class :character Class :character
## Median :69.0 Median :74.00 Mode :character Mode :character
## Mean :68.6 Mean :73.46
## 3rd Qu.:73.0 3rd Qu.:78.00
## Max. :94.0 Max. :97.00
## NA's :836 NA's :836
## defensive_work_rate crossing finishing heading_accuracy
## Length:183978 Min. : 1.00 Min. : 1.00 Min. : 1.00
## Class :character 1st Qu.:45.00 1st Qu.:34.00 1st Qu.:49.00
## Mode :character Median :59.00 Median :53.00 Median :60.00
## Mean :55.09 Mean :49.92 Mean :57.27
## 3rd Qu.:68.00 3rd Qu.:65.00 3rd Qu.:68.00
## Max. :95.00 Max. :97.00 Max. :98.00
## NA's :836 NA's :836 NA's :836
## short_passing volleys dribbling curve
## Min. : 3.00 Min. : 1.00 Min. : 1.00 Min. : 2.00
## 1st Qu.:57.00 1st Qu.:35.00 1st Qu.:52.00 1st Qu.:41.00
## Median :65.00 Median :52.00 Median :64.00 Median :56.00
## Mean :62.43 Mean :49.47 Mean :59.18 Mean :52.97
## 3rd Qu.:72.00 3rd Qu.:64.00 3rd Qu.:72.00 3rd Qu.:67.00
## Max. :97.00 Max. :93.00 Max. :97.00 Max. :94.00
## NA's :836 NA's :2713 NA's :836 NA's :2713
## free_kick_accuracy long_passing ball_control acceleration
## Min. : 1.00 Min. : 3.00 Min. : 5.00 Min. :10.00
## 1st Qu.:36.00 1st Qu.:49.00 1st Qu.:58.00 1st Qu.:61.00
## Median :50.00 Median :59.00 Median :67.00 Median :69.00
## Mean :49.38 Mean :57.07 Mean :63.39 Mean :67.66
## 3rd Qu.:63.00 3rd Qu.:67.00 3rd Qu.:73.00 3rd Qu.:77.00
## Max. :97.00 Max. :97.00 Max. :97.00 Max. :97.00
## NA's :836 NA's :836 NA's :836 NA's :836
## sprint_speed agility reactions balance shot_power
## Min. :12.00 Min. :11.00 Min. :17.0 Min. :12.00 Min. : 2.00
## 1st Qu.:62.00 1st Qu.:58.00 1st Qu.:61.0 1st Qu.:58.00 1st Qu.:54.00
## Median :69.00 Median :68.00 Median :67.0 Median :67.00 Median :65.00
## Mean :68.05 Mean :65.97 Mean :66.1 Mean :65.19 Mean :61.81
## 3rd Qu.:77.00 3rd Qu.:75.00 3rd Qu.:72.0 3rd Qu.:74.00 3rd Qu.:73.00
## Max. :97.00 Max. :96.00 Max. :96.0 Max. :96.00 Max. :97.00
## NA's :836 NA's :2713 NA's :836 NA's :2713 NA's :836
## jumping stamina strength long_shots
## Min. :14.00 Min. :10.00 Min. :10.00 Min. : 1.00
## 1st Qu.:60.00 1st Qu.:61.00 1st Qu.:60.00 1st Qu.:41.00
## Median :68.00 Median :69.00 Median :69.00 Median :58.00
## Mean :66.97 Mean :67.04 Mean :67.42 Mean :53.34
## 3rd Qu.:74.00 3rd Qu.:76.00 3rd Qu.:76.00 3rd Qu.:67.00
## Max. :96.00 Max. :96.00 Max. :96.00 Max. :96.00
## NA's :2713 NA's :836 NA's :836 NA's :836
## aggression interceptions positioning vision penalties
## Min. : 6.00 Min. : 1.00 Min. : 2.00 Min. : 1.00 Min. : 2
## 1st Qu.:51.00 1st Qu.:34.00 1st Qu.:45.00 1st Qu.:49.00 1st Qu.:45
## Median :64.00 Median :57.00 Median :60.00 Median :60.00 Median :57
## Mean :60.95 Mean :52.01 Mean :55.79 Mean :57.87 Mean :55
## 3rd Qu.:73.00 3rd Qu.:68.00 3rd Qu.:69.00 3rd Qu.:69.00 3rd Qu.:67
## Max. :97.00 Max. :96.00 Max. :96.00 Max. :97.00 Max. :96
## NA's :836 NA's :836 NA's :836 NA's :2713 NA's :836
## marking standing_tackle sliding_tackle gk_diving gk_handling
## Min. : 1.00 Min. : 1.00 Min. : 2 Min. : 1.0 Min. : 1.00
## 1st Qu.:25.00 1st Qu.:29.00 1st Qu.:25 1st Qu.: 7.0 1st Qu.: 8.00
## Median :50.00 Median :56.00 Median :53 Median :10.0 Median :11.00
## Mean :46.77 Mean :50.35 Mean :48 Mean :14.7 Mean :16.06
## 3rd Qu.:66.00 3rd Qu.:69.00 3rd Qu.:67 3rd Qu.:13.0 3rd Qu.:15.00
## Max. :96.00 Max. :95.00 Max. :95 Max. :94.0 Max. :93.00
## NA's :836 NA's :836 NA's :2713 NA's :836 NA's :836
## gk_kicking gk_positioning gk_reflexes
## Min. : 1 Min. : 1.00 Min. : 1.00
## 1st Qu.: 8 1st Qu.: 8.00 1st Qu.: 8.00
## Median :12 Median :11.00 Median :11.00
## Mean :21 Mean :16.13 Mean :16.44
## 3rd Qu.:15 3rd Qu.:15.00 3rd Qu.:15.00
## Max. :97 Max. :96.00 Max. :96.00
## NA's :836 NA's :836 NA's :836
Primer de tot, volem analitzar les variables relacionades amb la
qualitat general d’un jugador. Aquestes són overall_rating
i potential.
Mostrem la seua gràfica Box Plot:
box_plot_attr <- c("overall_rating", "potential")
df_player_attributes_aux <- df_player_attributes[, which(names(df_player_attributes) %in% box_plot_attr)]
boxplot(df_player_attributes_aux)
Veiem que són molt similars, però en general el valor del potencial dels jugadors és més alt que la seua valoració actual.
Ara estudiem les variables que tenen a veure amb la capacitat de xutar la pilota.
A continuació, es mostren les variables finishing,
heading_accuracy, volleys,
shot_power i long_shots:
box_plot_attr <- c("finishing",
"heading_accuracy",
"volleys",
"shot_power",
"long_shots")
df_player_attributes_aux <- df_player_attributes[, which(names(df_player_attributes) %in% box_plot_attr)]
boxplot(df_player_attributes_aux)
Es pot veure que finishing, volleys i
long_shots tenen una variabilitat molt elevada. En canvi,
heading_accuracy i shot_power mostren uns
valors molt més concentrats. A més, aquestes tenen un conjunt de mostres
que es poden considerar outliers, però les altres no.
També volem analitzar els atributs que tenen a veure amb la pilota
aturada. Aquests són curve, free_kick_accuracy
i penalties.
A continuació mostrem el seu Box Plot:
box_plot_attr <- c("curve",
"penalties",
"free_kick_accuracy")
df_player_attributes_aux <- df_player_attributes[, which(names(df_player_attributes) %in% box_plot_attr)]
boxplot(df_player_attributes_aux)
Podem veure que es generen uns gràfics molt similars. La principal
diferència és que en general els jugadors tenen menys precisió llençant
faltes que penals. També es veu com l’atribut penalties té
valors extrems per sota, per la qual cosa podem deduir que és més
complicat no saber llençar un penal que un tir lliure.
Ara visualitzem les variables que tenen a veure amb saber realitzar
una passada. Aquestes són crossing,
short_passing, long_passing i
vision.
Aquest és el seu Box Plot:
box_plot_attr <- c("crossing",
"short_passing",
"long_passing",
"vision")
df_player_attributes_aux <- df_player_attributes[, which(names(df_player_attributes) %in% box_plot_attr)]
boxplot(df_player_attributes_aux)
Veiem que la que té més variabilitat és la capacitat de fer una centrada, això ens indica que no és una habilitat que dominin tots els jugadors, segurament canvia molt segons la posició on juguin. La passada en curt sembla que és una qualitat que més jugadors dominen, ja que té un valor mitjà més elevat que les altres, els valors estan més concentrats i té molts outliers per sota.
En aquest apartat s’hi agrupen certs atributs dels jugadors que no encaixen en un grup concret. Malgrat això, encara guarden alguna relació entre ells.
Aquí es mostra la gràfica de dribbling,
ball_control, agility, reactions,
balance i jumping:
box_plot_attr <- c("dribbling",
"reactions",
"ball_control",
"agility",
"balance",
"jumping")
df_player_attributes_aux <- df_player_attributes[, which(names(df_player_attributes) %in% box_plot_attr)]
boxplot(df_player_attributes_aux)
Veiem que en general aquestes tenen uns valors més agrupats que la
resta de variables de la taula. També tenen uns valors mitjans
lleugerament superiors a les que hem analitzat fins ara. També es veu
com, a part de dribbling i ball control la
resta tenen uns valors mínims elevats. Llavors, podem deduir que és
bastant complicat que els jugadors no siguin àgils, no reaccionin bé, no
tinguin un bon equilibri o que no saltin gaire.
Aquí estudiem les variables que tenen a veure amb la força, la
velocitat i la resistència. Aquestes són acceleration,
sprint_speed, stamina i
strength.
A continuació es mostra la gràfica Box Plot:
box_plot_attr <- c("acceleration",
"sprint_speed",
"stamina",
"strength")
df_player_attributes_aux <- df_player_attributes[, which(names(df_player_attributes) %in% box_plot_attr)]
boxplot(df_player_attributes_aux)
Igual que en el cas anterior, també tenen una variabilitat més baixa i uns valors mitjans més alts. Però els seus mínims són més baixos.
Ara analitzem les qualitats que tenen relació amb defensar. Aquestes
són aggression, interceptions,
positioning, marking,
standing_tacke i sliding_tacke.
Aquestes són les seues gràfiques Box Plot:
box_plot_attr <- c("aggression",
"positioning",
"interceptions",
"marking",
"standing_tackle",
"sliding_tackle")
df_player_attributes_aux <- df_player_attributes[, which(names(df_player_attributes) %in% box_plot_attr)]
boxplot(df_player_attributes_aux)
Podem observar una variabilitat molt més alta, sobretot amb la capacitat d’interceptar una pilota, la capacitat de marcar a un oponent, la de robar una pilota i la de realitzar una segada. Això segurament es deu fet que són característiques purament defensives, i per tant, els davanters i alguns migcampistes no tindran aquestes habilitats.
En canvi, l’agressivitat i el posicionament no són purament defensives perquè un davanter pot necessitar ser agressiu a l’hora de lluitar una pilota i també li cal posicionar-se correctament al camp. Segurament, per això, veiem menys variabilitat.
Per acabar l’anàlisi de les variables numèriques ho fem amb les que
tenen a veure amb els porters. Aquestes variables són
gk_diving, gk_handling,
gk_kicking, gk_positioning i
gk_reflexes.
Mostrem la gràfica Box Plot:
box_plot_attr <- c("gk_diving",
"gk_handling",
"gk_kicking",
"gk_positioning",
"gk_reflexes")
df_player_attributes_aux <- df_player_attributes[, which(names(df_player_attributes) %in% box_plot_attr)]
boxplot(df_player_attributes_aux)
Podem veure uns valors mitjans molt baixos, una variabilitat molt baixa i molts valors extrems per dalt. Això, el que ens diu és que tots els jugadors (porters i jugadors de camp) tenen aquests atributs assignats, per tant, els jugadors de camp tenen una molt mala puntuació en aquests atributs. Això fa que es distorsioni la gràfica.
Estaria bé poder filtrar per tipus de jugador, però no tenim aquesta variable. El que es pot fer és deduir que els jugadors amb valors elevats en aquestes variables són porters i la resta no. És possible que es pugui cometre algun error, però no hauria de ser rellevant.
La variable preferred foot és una variable binària que
ens indica la preferència de cada jugador en utilitzar la cama esquerra
o la dreta.
Fem servir una gràfica circular per a veure quina és la preferida dels jugadors:
if (!require("dplyr")) install.packages("dplyr", repos = "http:/cran.us.r-project.org"); library("dplyr")
labels <- c("Esquerra", "Dreta")
df_preferred_foot <- df_player_attributes %>%
group_by(preferred_foot) %>%
count()
pie(df_preferred_foot$n, labels = labels)
Podem veure que tres quarts dels jugadors són dretans. Segons la pàgina web LEFTYFRETZ al món només el 12% de les persones és esquerrana, això voldria dir que en els futbolistes hi ha una proporció més alta d’esquerrans que en general. Un dels motius podria ser que a Europa la taxa d’esquerrans és més alta que en la resta del món i un altre que és més fàcil que algú sigui esquerrà si és un home. Malgrat tot, aquesta xifra sorprèn per què els motius citats no acaben d’explicar que n’hi hagi tants.
Per acabar l’anàlisi de la taula Player_Attributes
estudiem la distribució dels seus atributs categòrics amb un
histograma.
A continuació, es mostren els histogrames de les variables
attacking_work_rate i defensive_work_rate:
hist_list <- list()
dist_attr <- c("attacking_work_rate", "defensive_work_rate")
df_player_attributes_aux <- df_player_attributes[, which(names(df_player_attributes) %in% dist_attr)]
for(i in seq_len(ncol(df_player_attributes_aux))) {
col <- names(df_player_attributes_aux)[i]
ggp <- ggplot(df_player_attributes_aux, aes_string(x = col)) +
geom_histogram(stat = "count")
hist_list[[i]] <- ggp # afegim cada plot a la llista buida
}
multiplot(plotlist = hist_list, cols = 1)
Podem veure que en tots dos casos hi ha categories estranyes. Les que
tenen més valors són high, low i
medium. La resta semblen errors, ja que no corresponen amb
aquesta lògica ni tenen gaires valors. Per tant, caldrà tenir-ho en
compte a l’hora de netejar les dades.
summary(df_team_attributes)
## id team_fifa_api_id team_api_id date
## Min. : 1.0 Min. : 1 Min. : 1601 Length:1458
## 1st Qu.: 365.2 1st Qu.: 110 1st Qu.: 8458 Class :character
## Median : 729.5 Median : 485 Median : 8674 Mode :character
## Mean : 729.5 Mean : 17707 Mean : 9996
## 3rd Qu.:1093.8 3rd Qu.: 1900 3rd Qu.: 9904
## Max. :1458.0 Max. :112513 Max. :274581
##
## buildUpPlaySpeed buildUpPlaySpeedClass buildUpPlayDribbling
## Min. :20.00 Length:1458 Min. :24.00
## 1st Qu.:45.00 Class :character 1st Qu.:42.00
## Median :52.00 Mode :character Median :49.00
## Mean :52.46 Mean :48.61
## 3rd Qu.:62.00 3rd Qu.:55.00
## Max. :80.00 Max. :77.00
## NA's :969
## buildUpPlayDribblingClass buildUpPlayPassing buildUpPlayPassingClass
## Length:1458 Min. :20.00 Length:1458
## Class :character 1st Qu.:40.00 Class :character
## Mode :character Median :50.00 Mode :character
## Mean :48.49
## 3rd Qu.:55.00
## Max. :80.00
##
## buildUpPlayPositioningClass chanceCreationPassing chanceCreationPassingClass
## Length:1458 Min. :21.00 Length:1458
## Class :character 1st Qu.:46.00 Class :character
## Mode :character Median :52.00 Mode :character
## Mean :52.17
## 3rd Qu.:59.00
## Max. :80.00
##
## chanceCreationCrossing chanceCreationCrossingClass chanceCreationShooting
## Min. :20.00 Length:1458 Min. :22.00
## 1st Qu.:47.00 Class :character 1st Qu.:48.00
## Median :53.00 Mode :character Median :53.00
## Mean :53.73 Mean :53.97
## 3rd Qu.:62.00 3rd Qu.:61.00
## Max. :80.00 Max. :80.00
##
## chanceCreationShootingClass chanceCreationPositioningClass defencePressure
## Length:1458 Length:1458 Min. :23.00
## Class :character Class :character 1st Qu.:39.00
## Mode :character Mode :character Median :45.00
## Mean :46.02
## 3rd Qu.:51.00
## Max. :72.00
##
## defencePressureClass defenceAggression defenceAggressionClass defenceTeamWidth
## Length:1458 Min. :24.00 Length:1458 Min. :29.00
## Class :character 1st Qu.:44.00 Class :character 1st Qu.:47.00
## Mode :character Median :48.00 Mode :character Median :52.00
## Mean :49.25 Mean :52.19
## 3rd Qu.:55.00 3rd Qu.:58.00
## Max. :72.00 Max. :73.00
##
## defenceTeamWidthClass defenceDefenderLineClass
## Length:1458 Length:1458
## Class :character Class :character
## Mode :character Mode :character
##
##
##
##
Aquesta taula també té diverses variables numèriques i categòriques. Per això, farem com hem fet anteriorment i les agruparem per similitud.
Les primeres variables que analitzem són les que tenen a veure amb com un equip construeix el seu joc.
Aquí podem veure les gràfiques Box Plot de les variables
buildUpPlaySpeed, buildUpPlayDribbling i
buildUpPlayPassing:
box_plot_attr <- c("buildUpPlaySpeed",
"buildUpPlayDribbling",
"buildUpPlayPassing")
df_team_attributes_aux <- df_team_attributes[, which(names(df_team_attributes) %in% box_plot_attr)]
boxplot(df_team_attributes_aux)
Es pot observar com tenen una variabilitat molt gran i que tenen pocs o cap valor extrems.
Els segons atributs que analitzem són els que tenen a veure amb la
creació d’oportunitats. Aquests són chanceCreationPassing,
chanceCreationCrossing i `chanceCreationShooting.
Seguidament, podem veure les seues gràfiques Box Plot:
box_plot_attr <- c("chanceCreationPassing",
"chanceCreationCrossing",
"chanceCreationShooting")
df_team_attributes_aux <- df_team_attributes[, which(names(df_team_attributes) %in% box_plot_attr)]
boxplot(df_team_attributes_aux)
Són uns resultats bastant similars als anteriors, una gran variabilitat i amb pocs valors extrems.
Ara estudiem les variables que fan referència a l’agressivitat en
defensa. Aquestes són chanceCreationPassing,
chanceCreationCrossing i
chanceCreationShooting.
A continuació, es mostren els seus gràfics:
box_plot_attr <- c("defencePressure",
"defenceAggression",
"defenceTeamWidth")
df_team_attributes_aux <- df_team_attributes[, which(names(df_team_attributes) %in% box_plot_attr)]
boxplot(df_team_attributes_aux)
Tornem a tenir gràfics similars als anteriors. En tots aquests casos costa molt extreure’n conclusions per què són variables abstractes i amb resultats molt similars. L’únic que podem deduir és que els equips juguen de formes molt diferents i per això obtenim aquesta variabilitat tan elevada.
Mostrem l’histograma de les variables relacionades amb la forma de construir el joc:
hist_list <- list()
dist_attr <- c("buildUpPlaySpeedClass",
"buildUpPlayDribblingClass",
"buildUpPlayPassingClass",
"buildUpPlayPositioningClass")
df_team_attributes_aux <- df_team_attributes[, which(names(df_team_attributes) %in% dist_attr)]
for(i in seq_len(ncol(df_team_attributes_aux))) {
col <- names(df_team_attributes_aux)[i]
ggp <- ggplot(df_team_attributes_aux, aes_string(x = col)) +
geom_histogram(stat = "count")
hist_list[[i]] <- ggp # afegim cada plot a la llista buida
}
multiplot(plotlist = hist_list, cols = 1)
Podem veure que les distribucions són molt desiguals en totes les
variables, excepte en la buildUpPlayDribblingClass que hi
ha dos categories amb una quantitat similar. Això implica que els equips
construeixen el joc de forma semblant.
A continuació, es mostra l’histograma de les variables que tenen a veure amb la creació d’oportunitats:
hist_list <- list()
dist_attr <- c("chanceCreationPassingClass",
"chanceCreationCrossingClass",
"chanceCreationShootingClass",
"chanceCreationPositioningClass")
df_team_attributes_aux <- df_team_attributes[, which(names(df_team_attributes) %in% dist_attr)]
for(i in seq_len(ncol(df_team_attributes_aux))) {
col <- names(df_team_attributes_aux)[i]
ggp <- ggplot(df_team_attributes_aux, aes_string(x = col)) +
geom_histogram(stat = "count")
hist_list[[i]] <- ggp # afegim cada plot a la llista buida
}
multiplot(plotlist = hist_list, cols = 1)
Un altre cop, veiem com en totes les variables hi ha una categoria
que destaca clarament, que acostuma a ser la Normal.
Finalment, es genera l’histograma pels atributs defensius:
hist_list <- list()
dist_attr <- c("defencePressureClass",
"defenceAggressionClass",
"defenceTeamWidthClass",
"defenceDefenderLineClass")
df_team_attributes_aux <- df_team_attributes[, which(names(df_team_attributes) %in% dist_attr)]
for(i in seq_len(ncol(df_team_attributes_aux))) {
col <- names(df_team_attributes_aux)[i]
ggp <- ggplot(df_team_attributes_aux, aes_string(x = col)) +
geom_histogram(stat = "count")
hist_list[[i]] <- ggp # afegim cada plot a la llista buida
}
multiplot(plotlist = hist_list, cols = 1)
Tampoc hi ha sorpreses, la majoria dels equips comparteixen les mateixes categories. Això sorprèn per què en l’anàlisi de les variables numèriques ens ha costat veure que tinguessin valors similars, però quan s’agrupen en ca veiem que són pocs els equips que es mouen de la normalitat.
L’anàlisi multivariant és una branca de l’estadística que consisteix a examinar diverses variables alhora.
Com hem vist en l’anàlisi preliminar, no totes les taules tenen
atributs que es puguin analitzar. Algunes només són taules auxiliars que
ens serveixen per a entendre millor les dades. Per tant, només es faran
servir aquelles taules que tinguin atributs rellevants. Aquestes són
Match, Player, Player_Attributes
i Team_Attributes.
En aquesta secció s’analitza la matriu de correlacions per a cada una de les taules. Aquesta matriu ens indica la correlació que hi ha entre les diverses variables.
Fem servir les següents funcions que mostren a la part baixa de la matriu l’scatter plot de dos variables, a la diagonal mostren la distribució de la variable i a la part alta de la matriu mostren la correlació entre dos variables:
# https://r-coder.com/correlation-plot-r/
panel_hist <- function(x, ...) {
usr <- par("usr")
on.exit(par(usr))
par(usr = c(usr[1:2], 0, 1.5))
his <- hist(x, plot = FALSE)
breaks <- his$breaks
nB <- length(breaks)
y <- his$counts
y <- y / max(y)
rect(breaks[-nB], 0, breaks[-1], y, col = rgb(0.8, 0.5, 0.5, alpha = 1.0), ...)
}
panel_corr <- function(x, y, digits = 2, prefix = "", cex_cor, ...) {
usr <- par("usr")
on.exit(par(usr))
par(usr = c(0, 1, 0, 1))
corr <- abs(cor(x, y)) # Remove abs function if desired
txt <- paste0(prefix, format(c(corr, 0.123456789), digits = digits)[1])
if (missing(cex_cor)) {
cex_cor <- 0.4 / strwidth(txt)
}
text(0.5, 0.5, txt,
cex = 1 + cex_cor * corr) # Resize the text by level of correlation
}
corr_attr <- c("home_team_goal",
"away_team_goal")
df_match_corr <- df_match[, which(names(df_match) %in% corr_attr)]
# Plot correlation matrix
pairs(df_match_corr,
upper.panel = panel_corr,
diag.panel = panel_hist)
En aquesta taula només tenim dos variables numèriques i veiem que aquestes no estan correlacionades.
corr_attr <- c("height", "weight")
df_player_corr <- df_player[, which(names(df_player) %in% corr_attr)]
# Plot correlation matrix
pairs(df_player_corr,
upper.panel = panel_corr,
diag.panel = panel_hist)
Aquesta taula també té només dos atributs numèrics. Veiem que estan força correlacionats. És lògic, ja que, com més alçada més pesa una persona.
corr_attr <- c("overall_rating", "potential")
df_player_attributes_corr <- df_player_attributes[, which(names(df_player_attributes) %in% corr_attr)]
df_player_attributes_corr <- na.omit(df_player_attributes_corr)
# Plot correlation matrix
pairs(df_player_attributes_corr,
upper.panel = panel_corr,
diag.panel = panel_hist)
En aquest cas, tenim només les variables overall_rating
i potential. I veiem que també estan bastant
correlacionades. És lògic perquè com més valoració global té un jugador,
més pot arribar a millorar.
corr_attr <- c("buildUpPlaySpeed",
"buildUpPlayDribbling",
"buildUpPlayPassing",
"chanceCreationPassing",
"chanceCreationCrossing",
"chanceCreationShooting",
"defencePressure",
"defenceAggression",
"defenceTeamWidth")
df_team_attributes_corr <- df_team_attributes[, which(names(df_team_attributes) %in% corr_attr)]
df_team_attributes_corr <- na.omit(df_team_attributes_corr)
# Plot correlation matrix
pairs(df_team_attributes_corr,
upper.panel = panel_corr,
diag.panel = panel_hist)
En aquesta taula ja tenim moltes més variables. En general hi ha cap
parell de variables amb una forta correlació. És estrany perquè hi ha
variables amb una temàtica semblant. Els parells amb més correlació són
defencePressure i defenceAggression,
defencePressure i defenceTeamWidth, i
buildUpPlaySpeed i buildUpPlayPassing.
L’objectiu d’aquesta secció és deixar les dades polides i ben netes per poder aplicar els algorismes de mineria de dades desitjats.
El primer pas que duem a terme és el d’esborrar variables buides o innecessàries.
Gràcies a l’anterior anàlisi ja coneixem quines variables contenen massa registres buits per a fer-les servir. També hem observat que algunes estan mal formatades, o bé contenen paràmetres incorrectes o bé hi ha categories de més. S’aplicarà la millor estratègia en cada cas.
Com s’ha explicat anteriorment, les quotes d’apostes no ens interessen, per això les podem suprimir del nostre dataset.
Abans de res, ens guardem dataframe original per si el volem recuperar més endavant:
df_match_org <- df_match
Ara procedim a suprimir les variables d’apostes:
odds_attr <- c("B365H", "B365D", "B365A",
"BWH", "BWD", "BWA",
"IWH", "IWD", "IWA",
"LBH", "LBD", "LBA",
"PSH", "PSD", "PSA",
"WHH", "WHD", "WHA",
"SJH", "SJD", "SJA",
"VCH", "VCD", "VCA",
"GBH", "GBD", "GBA",
"BSH", "BSD", "BSA")
df_match <- df_match[, -which(names(df_match) %in% odds_attr)]
I a continuació, suprimim les variables goal,
shoton, shotoff, foulcommit,
card, cross, corner i
possession que tenen massa valors NA i
impedeixen poder fer cap anàlisi:
empty_attr <- c("goal",
"shoton",
"shotoff",
"foulcommit",
"card",
"cross",
"corner",
"possession")
df_match <- df_match[, -which(names(df_match) %in% empty_attr)]
Com hem vist en l’anàlisi univariant, aquesta taula té dos atributs
amb categories equivocades. Aquests són defensive_work_rate
i attacking_work_rate.
Ens guardem el dataframe original:
df_player_attributes_org <- df_player_attributes
Convertim en NA les categories que no són
high, low i medium:
keep_values <- c("high", "medium", "low")
df_player_attributes$defensive_work_rate[!(df_player_attributes$defensive_work_rate %in% keep_values)] <- NA
df_player_attributes$attacking_work_rate[!(df_player_attributes$attacking_work_rate %in% keep_values)] <- NA
És possible que després de la conversió hi hagi massa valors
NA en aquestes variables. Tornem a comprovar la quantitat
de valors buits que hi ha en la taula:
sort(colMeans(is.na(df_player_attributes) | df_player_attributes == ""), decreasing = TRUE)
## defensive_work_rate attacking_work_rate volleys curve
## 0.041629977 0.040852711 0.014746328 0.014746328
## agility balance jumping vision
## 0.014746328 0.014746328 0.014746328 0.014746328
## sliding_tackle overall_rating potential preferred_foot
## 0.014746328 0.004544022 0.004544022 0.004544022
## crossing finishing heading_accuracy short_passing
## 0.004544022 0.004544022 0.004544022 0.004544022
## dribbling free_kick_accuracy long_passing ball_control
## 0.004544022 0.004544022 0.004544022 0.004544022
## acceleration sprint_speed reactions shot_power
## 0.004544022 0.004544022 0.004544022 0.004544022
## stamina strength long_shots aggression
## 0.004544022 0.004544022 0.004544022 0.004544022
## interceptions positioning penalties marking
## 0.004544022 0.004544022 0.004544022 0.004544022
## standing_tackle gk_diving gk_handling gk_kicking
## 0.004544022 0.004544022 0.004544022 0.004544022
## gk_positioning gk_reflexes id player_fifa_api_id
## 0.004544022 0.004544022 0.000000000 0.000000000
## player_api_id date
## 0.000000000 0.000000000
Veiem que el percentatge de valors buits segueix sent molt baix, per tant, no ens preocupem.
En aquest pas la intenció és reduir el nombre total de taules. Hem pogut veure que hi ha moltes taules redundants. Per això, sempre que es pugui, unirem aquestes taules.
Com que les taules Country i League són
molt similars i fan referència als mateixos registres té molt sentit
unir-les.
Ens guardem el dataframe original:
df_league_org <- df_league
Realitzem un outer-join amb totes dos taules i la guardem a
la taula League:
df_league <- merge(x = df_league, y = df_country, by = "id", all = TRUE)
Esborrem la columna country_id:
df_league <- df_league[, -which(names(df_league) == "country_id")]
I finalment, canviem els noms a les columnes name.x i
name.y:
colnames(df_league) <- c("id", "league_name", "country_name")
df_league
## id league_name country_name
## 1 1 Belgium Jupiler League Belgium
## 2 1729 England Premier League England
## 3 4769 France Ligue 1 France
## 4 7809 Germany 1. Bundesliga Germany
## 5 10257 Italy Serie A Italy
## 6 13274 Netherlands Eredivisie Netherlands
## 7 15722 Poland Ekstraklasa Poland
## 8 17642 Portugal Liga ZON Sagres Portugal
## 9 19694 Scotland Premier League Scotland
## 10 21518 Spain LIGA BBVA Spain
## 11 24558 Switzerland Super League Switzerland
Basant-nos en el contingut d’aquestes dos taules, semblaria lògic pensar que haurien d’estar unides en una de sola.
Però, una cosa que ens sorprèn és que la taula Player té
11.060 registres i la taula Player_Attributes en té
183.978. És una diferència molt gran.
Estudiem si la taula Player_Attributes conté registres
duplicats. Ho fem amb la comanda d’R duplicated que indica
si un registre està duplicat o no:
any(duplicated(df_player_attributes$id))
## [1] FALSE
any(duplicated(df_player_attributes$player_api_id))
## [1] TRUE
any(duplicated(df_player_attributes$player_fifa_api_id))
## [1] TRUE
Veiem que la variable id no conté cap valor repetit, com
és lògic. Però, en canvi, la variable player_api_id sí que
conté duplicats, però en tractar-se d’un identificador haurien de ser
valors únics.
Hem de tenir en compte que hi ha una variable date, fins
ara no sabíem ben bé què indicava, però ara podem deduir que ens mostra
els atributs d’un jugador en una data determinada.
Per tant, si volguéssim podríem estudiar l’evolució dels jugadors en el temps, encara que les dates no són gaire consistents entre jugadors. També caldrà tenir-ho en compte a l’hora de calcular nous atributs.
Pel que fa a unir les dos taules no hi ha problema, és cert que no sabem en quina data es van mesurar el pes i l’alçada dels jugadors però no creiem que hi hagi gaire diferència.
Procedim a fer un outer join de les taules
Player i Player_Attributes que emmagatzemem en
la taula Player_Attributes:
df_player_attributes_org <- df_player_attributes
df_player_attributes <- merge(x = df_player_attributes, y = df_player, by = c("player_api_id", "player_fifa_api_id"), all = TRUE)
Aquestes dos taules són molt similars a les anteriors, ja que, la
taula Team_Attributes conté moltes més observacions que la
taula Team, però, és perquè cada equip té múltiples
registres segons la data.
Unim totes dos taules fent servir el mètode anterior:
df_team_attributes_org <- df_team_attributes
df_team_attributes <- merge(x = df_team_attributes, y = df_team, by = c("team_api_id", "team_fifa_api_id"), all = TRUE)
L’últim pas consisteix a crear nous atributs que serveixin per a facilitat l’anàlisi.
En aquesta taula ens interessa crear una variable que indiqui els gols totals que s’han marcat al partit. Es calcularà sumant els gols de l’equip local i els gols de l’equip visitant.
Creem la nova variable total_goals sumant
home_team_goal i away_team_goal:
df_match$total_goals <- (df_match$home_team_goal + df_match$away_team_goal)
summary(df_match$total_goals)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.000 2.000 3.000 2.706 4.000 12.000
I comprovem que el càlcul és correcte:
head(df_match[c("home_team_goal", "away_team_goal", "total_goals")])
## home_team_goal away_team_goal total_goals
## 1 1 1 2
## 2 0 0 0
## 3 0 3 3
## 4 5 0 5
## 5 1 3 4
## 6 1 1 2
En aquesta taula podem calcular múltiples atributs nous relacionats amb els jugadors.
El primer que podem fer és crear una variable que combini l’alçada i el pes. Podem fer servir el càlcul de l’índex de massa corporal (IMC) o body mass index (BMI), en anglès.
La fórmula és la següent: \[ {massa (lb)} \over {(alçada/100)^2 (cm) · 2.2046} \]
Afegim la variable bmi:
df_player_attributes$bmi <- ((df_player_attributes$weight) / (((df_player_attributes$height/100)**2)*2.2046))
summary(df_player_attributes$bmi)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 17.59 22.18 23.10 23.12 23.99 30.87 212
Convertim a int les variables birth_day,
birth_month i birth_year:
df_player_attributes$birth_day <- as.integer(df_player_attributes$birth_day)
df_player_attributes$birth_month <- as.integer(df_player_attributes$birth_month)
df_player_attributes$birth_year <- as.integer(df_player_attributes$birth_year)
str(df_player_attributes)
## 'data.frame': 183978 obs. of 51 variables:
## $ player_api_id : int 2625 2625 2625 2625 2625 2625 2625 2625 2625 2625 ...
## $ player_fifa_api_id : int 148544 148544 148544 148544 148544 148544 148544 148544 148544 148544 ...
## $ id.x : int 139845 139849 139846 139847 139844 139857 139856 139855 139850 139848 ...
## $ date : chr "2014-11-07 00:00:00" "2013-02-15 00:00:00" "2014-09-18 00:00:00" "2013-06-07 00:00:00" ...
## $ overall_rating : int 61 58 61 61 61 63 63 60 58 61 ...
## $ potential : int 61 58 61 61 61 64 64 64 58 61 ...
## $ preferred_foot : chr "right" "right" "right" "right" ...
## $ attacking_work_rate: chr "medium" "medium" "medium" "medium" ...
## $ defensive_work_rate: chr "medium" "medium" "medium" "medium" ...
## $ crossing : int 50 52 50 50 50 48 48 48 52 50 ...
## $ finishing : int 47 47 47 47 47 48 48 48 47 47 ...
## $ heading_accuracy : int 46 46 46 46 46 47 47 47 46 46 ...
## $ short_passing : int 52 53 52 52 52 64 64 64 53 52 ...
## $ volleys : int 39 37 39 39 39 38 38 38 37 39 ...
## $ dribbling : int 50 56 50 50 50 57 57 57 56 50 ...
## $ curve : int 51 49 51 51 51 50 50 50 49 51 ...
## $ free_kick_accuracy : int 50 50 50 50 50 46 51 51 50 50 ...
## $ long_passing : int 64 66 64 64 64 67 67 67 66 64 ...
## $ ball_control : int 62 58 62 62 62 57 57 57 58 62 ...
## $ acceleration : int 67 67 67 67 67 67 67 67 67 67 ...
## $ sprint_speed : int 63 63 63 63 63 64 64 64 63 63 ...
## $ agility : int 74 74 74 74 74 59 59 59 74 74 ...
## $ reactions : int 49 49 49 49 49 52 52 52 49 49 ...
## $ balance : int 76 76 76 76 76 49 49 49 76 76 ...
## $ shot_power : int 68 68 68 68 68 61 61 61 68 68 ...
## $ jumping : int 63 63 63 63 63 56 56 56 55 63 ...
## $ stamina : int 77 77 77 77 77 78 78 78 77 77 ...
## $ strength : int 56 56 56 56 56 56 56 56 56 56 ...
## $ long_shots : int 54 58 54 54 54 59 59 59 58 54 ...
## $ aggression : int 71 71 71 71 71 72 72 72 71 71 ...
## $ interceptions : int 64 64 64 64 64 52 52 52 64 64 ...
## $ positioning : int 49 49 49 49 49 55 55 55 49 49 ...
## $ vision : int 55 55 55 55 55 56 56 56 55 55 ...
## $ penalties : int 66 66 66 66 66 46 46 46 66 66 ...
## $ marking : int 62 63 62 62 62 64 64 64 63 62 ...
## $ standing_tackle : int 63 63 63 63 63 66 66 66 63 63 ...
## $ sliding_tackle : int 54 52 54 54 54 63 63 63 52 54 ...
## $ gk_diving : int 12 12 12 12 12 14 14 14 12 12 ...
## $ gk_handling : int 11 11 11 11 11 11 24 24 11 11 ...
## $ gk_kicking : int 6 6 6 6 6 67 67 67 6 6 ...
## $ gk_positioning : int 8 8 8 8 8 9 24 24 8 8 ...
## $ gk_reflexes : int 8 8 8 8 8 10 24 24 8 8 ...
## $ id.y : int 8372 8372 8372 8372 8372 8372 8372 8372 8372 8372 ...
## $ player_name : chr "Patryk Rachwal,18" "Patryk Rachwal,18" "Patryk Rachwal,18" "Patryk Rachwal,18" ...
## $ birthday : chr "1981-01-27 00:00:00" "1981-01-27 00:00:00" "1981-01-27 00:00:00" "1981-01-27 00:00:00" ...
## $ height : num 175 175 175 175 175 ...
## $ weight : int 154 154 154 154 154 154 154 154 154 154 ...
## $ birth_year : int 1981 1981 1981 1981 1981 1981 1981 1981 1981 1981 ...
## $ birth_month : int 1 1 1 1 1 1 1 1 1 1 ...
## $ birth_day : int 27 27 27 27 27 27 27 27 27 27 ...
## $ bmi : num 22.7 22.7 22.7 22.7 22.7 ...
També podem afegir el càlcul de l’edat dels jugadors l’any 2016:
df_player_attributes$age <- (2016 - df_player_attributes$birth_year)
summary(df_player_attributes$age)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 17.00 26.00 29.00 29.21 33.00 49.00 212
La finalitat de normalitzar els valors és situar les dades sobre una escala de valors equivalents. D’aquesta forma es poden comparar tots els atributs, encara que originalment tinguessin rangs diferents.
En aquest cas, normalitzem els valors numèrics de les taules
utilitzant la normalització basada en la desviació estandard. A més,
també suprimim els registres que contenen NA.
num_attr <- c("home_team_goal",
"away_team_goal",
"total_goals")
df_match_aux <- df_match[, num_attr]
df_match_nor <- as.data.frame(scale(df_match_aux))
df_match_nor <- na.omit(df_match_nor)
summary(df_match_nor)
## home_team_goal away_team_goal total_goals
## Min. :-1.1908 Min. :-1.0165 Min. :-1.6177
## 1st Qu.:-0.4198 1st Qu.:-1.0165 1st Qu.:-0.4219
## Median :-0.4198 Median :-0.1409 Median : 0.1761
## Mean : 0.0000 Mean : 0.0000 Mean : 0.0000
## 3rd Qu.: 0.3511 3rd Qu.: 0.7347 3rd Qu.: 0.7740
## Max. : 6.5184 Max. : 6.8637 Max. : 5.5574
num_attr <- c("overall_rating", "potential",
"crossing", "finishing",
"heading_accuracy", "short_passing",
"volleys", "dribbling",
"curve", "free_kick_accuracy",
"long_passing", "ball_control",
"acceleration", "sprint_speed",
"agility", "reactions",
"balance", "shot_power",
"jumping", "stamina",
"strength", "long_shots",
"aggression", "interceptions",
"positioning", "vision",
"penalties", "marking",
"standing_tackle", "sliding_tackle",
"gk_diving", "gk_handling",
"gk_kicking", "gk_positioning",
"gk_reflexes", "height",
"weight", "birth_year",
"birth_month", "birth_day",
"bmi", "age")
df_player_attributes_aux <- df_player_attributes[, num_attr]
df_player_attributes_nor <- as.data.frame(scale(df_player_attributes_aux))
df_player_attributes_nor <- na.omit(df_player_attributes_nor)
summary(df_player_attributes_nor)
## overall_rating potential crossing
## Min. :-5.056002 Min. :-5.227387 Min. :-3.136902
## 1st Qu.:-0.653306 1st Qu.:-0.676603 1st Qu.:-0.585014
## Median : 0.056807 Median : 0.081861 Median : 0.226951
## Mean : 0.002695 Mean : 0.001331 Mean : 0.001561
## 3rd Qu.: 0.624897 3rd Qu.: 0.688632 3rd Qu.: 0.748928
## Max. : 3.607368 Max. : 3.570795 Max. : 2.314859
## finishing heading_accuracy short_passing
## Min. :-2.5695591 Min. :-3.412356 Min. :-4.186937
## 1st Qu.:-0.8362479 1st Qu.:-0.501308 1st Qu.:-0.382531
## Median : 0.1617191 Median : 0.165807 Median : 0.181085
## Mean : 0.0005347 Mean :-0.000257 Mean : 0.001961
## 3rd Qu.: 0.7920141 3rd Qu.: 0.650982 3rd Qu.: 0.674248
## Max. : 2.4728007 Max. : 2.470387 Max. : 2.435548
## volleys dribbling curve
## Min. :-2.654842 Min. :-3.278455 Min. :-2.791754
## 1st Qu.:-0.792504 1st Qu.:-0.404355 1st Qu.:-0.655446
## Median : 0.138666 Median : 0.271904 Median : 0.166212
## Mean :-0.000247 Mean : 0.002797 Mean :-0.000454
## 3rd Qu.: 0.795961 3rd Qu.: 0.722743 3rd Qu.: 0.768760
## Max. : 2.384427 Max. : 2.131615 Max. : 2.247743
## free_kick_accuracy long_passing ball_control
## Min. :-2.7131919 Min. :-3.756297 Min. :-3.842215
## 1st Qu.:-0.7504004 1st Qu.:-0.560624 1st Qu.:-0.288805
## Median : 0.0347162 Median : 0.134088 Median : 0.237626
## Mean :-0.0006399 Mean : 0.000393 Mean : 0.001989
## 3rd Qu.: 0.7637530 3rd Qu.: 0.689857 3rd Qu.: 0.632449
## Max. : 2.6704647 Max. : 2.773992 Max. : 2.211742
## acceleration sprint_speed agility
## Min. :-4.441031 Min. :-4.459227 Min. :-4.243356
## 1st Qu.:-0.512916 1st Qu.:-0.481414 1st Qu.:-0.615296
## Median : 0.103259 Median : 0.075479 Median : 0.156631
## Mean : 0.002208 Mean : 0.002237 Mean :-0.000605
## 3rd Qu.: 0.719434 3rd Qu.: 0.711930 3rd Qu.: 0.696980
## Max. : 2.259871 Max. : 2.303055 Max. : 2.318028
## reactions balance shot_power
## Min. :-5.363355 Min. :-4.071709 Min. :-3.706718
## 1st Qu.:-0.557453 1st Qu.:-0.550363 1st Qu.:-0.483939
## Median : 0.097898 Median : 0.138596 Median : 0.197803
## Mean : 0.002677 Mean :-0.000522 Mean : 0.002184
## 3rd Qu.: 0.644023 3rd Qu.: 0.674453 3rd Qu.: 0.693615
## Max. : 3.265424 Max. : 2.358575 Max. : 2.181051
## jumping stamina strength long_shots
## Min. :-4.812422 Min. :-4.332504 Min. :-4.75673 Min. :-2.849641
## 1st Qu.:-0.633162 1st Qu.:-0.458673 1st Qu.:-0.61501 1st Qu.:-0.671825
## Median : 0.093666 Median : 0.148987 Median : 0.13050 Median : 0.253747
## Mean : 0.000782 Mean : 0.000357 Mean : 0.00065 Mean : 0.001224
## 3rd Qu.: 0.638787 3rd Qu.: 0.680690 3rd Qu.: 0.71034 3rd Qu.: 0.743755
## Max. : 2.637563 Max. : 2.199839 Max. : 2.36703 Max. : 2.322672
## aggression interceptions positioning
## Min. :-3.415145 Min. :-2.622567 Min. :-2.915528
## 1st Qu.:-0.618293 1st Qu.:-0.925920 1st Qu.:-0.584689
## Median : 0.189686 Median : 0.205177 Median : 0.228395
## Mean :-0.000568 Mean :-0.004663 Mean :-0.003868
## 3rd Qu.: 0.749056 3rd Qu.: 0.822140 3rd Qu.: 0.716245
## Max. : 2.240710 Max. : 2.261719 Max. : 2.125589
## vision penalties marking
## Min. :-3.755496 Min. :-3.409380 Min. :-2.1562540
## 1st Qu.:-0.585942 1st Qu.:-0.643487 1st Qu.:-1.0256540
## Median : 0.140415 Median : 0.128390 Median : 0.1520543
## Mean :-0.000352 Mean :-0.004523 Mean :-0.0006505
## 3rd Qu.: 0.734706 3rd Qu.: 0.707298 3rd Qu.: 0.9057876
## Max. : 2.583613 Max. : 2.636990 Max. : 2.2248209
## standing_tackle sliding_tackle gk_diving
## Min. :-2.2971483 Min. :-2.129818 Min. :-0.812571
## 1st Qu.:-0.9938349 1st Qu.:-1.064943 1st Qu.:-0.456815
## Median : 0.2629315 Median : 0.231427 Median :-0.278936
## Mean :-0.0000281 Mean : 0.000648 Mean : 0.000394
## 3rd Qu.: 0.8680412 3rd Qu.: 0.879612 3rd Qu.:-0.101058
## Max. : 2.0782608 Max. : 2.175981 Max. : 4.701655
## gk_handling gk_kicking gk_positioning gk_reflexes
## Min. :-0.949345 Min. :-0.93219 Min. :-0.93993 Min. :-0.897854
## 1st Qu.:-0.508188 1st Qu.:-0.60590 1st Qu.:-0.50513 1st Qu.:-0.490834
## Median :-0.319121 Median :-0.41945 Median :-0.31878 Median :-0.316397
## Mean :-0.005326 Mean :-0.01712 Mean :-0.00525 Mean :-0.004893
## 3rd Qu.:-0.067031 3rd Qu.:-0.27960 3rd Qu.:-0.07032 3rd Qu.:-0.083814
## Max. : 4.848713 Max. : 3.54271 Max. : 4.96099 Max. : 4.625994
## height weight birth_year
## Min. :-3.814343 Min. :-3.430241 Min. :-3.65544
## 1st Qu.:-0.637160 1st Qu.:-0.647351 1st Qu.:-0.57270
## Median : 0.157136 Median :-0.051018 Median : 0.04384
## Mean : 0.000692 Mean : 0.000209 Mean : 0.02026
## 3rd Qu.: 0.554284 3rd Qu.: 0.677834 3rd Qu.: 0.86591
## Max. : 4.128615 Max. : 4.918428 Max. : 2.51003
## birth_month birth_day bmi
## Min. :-1.4446531 Min. :-1.6368437 Min. :-3.928781
## 1st Qu.:-0.8621775 1st Qu.:-0.8457496 1st Qu.:-0.704882
## Median : 0.0115360 Median :-0.0546556 Median :-0.012492
## Mean :-0.0005017 Mean :-0.0002661 Mean :-0.000524
## 3rd Qu.: 0.8852494 3rd Qu.: 0.8494518 3rd Qu.: 0.652160
## Max. : 1.7589629 Max. : 1.7535593 Max. : 5.799199
## age
## Min. :-2.51003
## 1st Qu.:-0.86591
## Median :-0.04384
## Mean :-0.02026
## 3rd Qu.: 0.57270
## Max. : 3.65544
num_attr <- c("buildUpPlaySpeed",
"buildUpPlayDribbling",
"buildUpPlayPassing",
"chanceCreationPassing",
"chanceCreationCrossing",
"chanceCreationShooting",
"defencePressure",
"defenceAggression",
"defenceTeamWidth")
df_team_attributes_aux <- df_team_attributes[, num_attr]
df_team_attributes_nor <- as.data.frame(scale(df_team_attributes_aux))
df_team_attributes_nor <- na.omit(df_team_attributes_nor)
summary(df_team_attributes_nor)
## buildUpPlaySpeed buildUpPlayDribbling buildUpPlayPassing
## Min. :-2.2919 Min. :-2.54253 Min. :-2.61473
## 1st Qu.:-0.3865 1st Qu.:-0.68270 1st Qu.:-0.68744
## Median : 0.1332 Median : 0.04057 Median : 0.13855
## Mean : 0.1419 Mean : 0.00000 Mean : 0.08093
## 3rd Qu.: 0.8261 3rd Qu.: 0.66051 3rd Qu.: 0.59742
## Max. : 2.3851 Max. : 2.93364 Max. : 2.80005
## chanceCreationPassing chanceCreationCrossing chanceCreationShooting
## Min. :-3.00800 Min. :-2.77193 Min. :-3.0955
## 1st Qu.:-0.49854 1st Qu.:-0.51700 1st Qu.:-0.7716
## Median : 0.08056 Median :-0.06601 Median :-0.1907
## Mean : 0.04050 Mean :-0.05752 Mean :-0.2554
## 3rd Qu.: 0.65967 3rd Qu.: 0.56537 3rd Qu.: 0.2935
## Max. : 2.39699 Max. : 2.36932 Max. : 2.5205
## defencePressure defenceAggression defenceTeamWidth
## Min. :-2.25058 Min. :-2.59303 Min. :-2.42157
## 1st Qu.:-0.68612 1st Qu.:-0.53923 1st Qu.:-0.43718
## Median :-0.09945 Median :-0.12847 Median :-0.01941
## Mean :-0.07666 Mean :-0.07807 Mean :-0.05999
## 3rd Qu.: 0.48721 3rd Qu.: 0.48767 3rd Qu.: 0.39835
## Max. : 2.54056 Max. : 2.33610 Max. : 2.17386
Un cop hem dut a terme les anteriors anàlisis i transformacions del nostre joc de dades, encara podem aplicar-li mètodes de reducció de la dimensionalitat. La finalitat és reduir el nombre d’atributs que tenim actualment, sense que el model perdi qualitat.
La tècnica que fem servir per a reduir el nombre de variables és el Principal Component Analysis (PCA). Fent servir aquesta tècnica es pretén crear combinacions lineals de les característiques del dataset original. Aquestes noves combinacions són anomenades components. El que es fa és incorporar els components principals, que són aquells que acumulen una variabilitat superior al 90%.
En aquesta secció calculem els components principals per a cadascuna de les taules que han sigut normalitzades anteriorment, fem una visualització dels resultats i els interpretem.
En aquest exercici fem servir el mètode de Kàiser per decidir quines variables hem d’escollir. Ens quedem amb totes les variables que tinguin una variància superior a 1. Fent ús d’aquest mètode és fàcil que sobreestimem el nombre de components, per tant, caldrà tenir-ho en compte quan analitzem els resultats.
Utilitzem la funció prcomp per fer una anàlisi de les
components principals de la taula:
match_pca <- prcomp(df_match_nor, center = TRUE, scale. = TRUE)
summary(match_pca)
## Importance of components:
## PC1 PC2 PC3
## Standard deviation 1.3916 1.0312 1.142e-13
## Proportion of Variance 0.6455 0.3545 0.000e+00
## Cumulative Proportion 0.6455 1.0000 1.000e+00
Podem veure que tenim 4 components principals, i el primer de tots explica el 0,4843 de variabilitat del total de dades, en canvi, el quart no n’explica res.
Per poder fer servir el mètode de Kàiser hem de calcular la variància dels components principals a partir de la desviació estàndard:
var_acc <- match_pca$sdev^2
var_acc
## [1] 1.936573e+00 1.063427e+00 1.304826e-26
var_acc > 1
## [1] TRUE TRUE FALSE
Seguint aquest mètode, escollim els components PCA1 i PCA2.
La funció get_pca_var(...) ens retorna un objecte que es
pot fer servir per seleccionar els elements principals que hem triat. En
aquest cas mostrem les coordenades que ens serveixen per Generació del
un diagrama de dispersió:
if (!require("factoextra")) install.packages("factoextra"); library("factoextra")
match_var <- get_pca_var(match_pca)
head(match_var$coord[, 1:2])
## Dim.1 Dim.2
## home_team_goal 0.7385428 0.674206562
## away_team_goal 0.6254800 -0.780240174
## total_goals 0.9999509 -0.009906259
A continuació, mostrem un histograma per veure el pes total dels components sobre el conjunt de dades:
if (!require("corrplot")) install.packages("corrplot"); library("corrplot")
ev <- get_eig(match_pca)
ev
## eigenvalue variance.percent cumulative.variance.percent
## Dim.1 1.936573e+00 6.455242e+01 64.55242
## Dim.2 1.063427e+00 3.544758e+01 100.00000
## Dim.3 1.304826e-26 4.349421e-25 100.00000
fviz_eig(match_pca)
També podem visualitzar la qualitat de representació de les variables
en el mapa de factors. Ho fem accedint a la variable cos2
de l’objecte que hem creat prèviament.
corrplot(match_var$cos2[, 1:2], is.corr = FALSE)
fviz_cos2(match_pca, choice = "var", axes = 1:2)
Veiem que totes 3 variables tenen un cos2 igual a
1. Per tant, sabem que amb 2 components es poden
representar perfectament. Això té molt sentit perquè les 3 estan molt
relacionades entre elles.
Finalment, també visualitzem la contribució de les variables PCA fent
servir la funció fviz_pca_var:
fviz_pca_var(match_pca,
col.var = "contrib",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE
)
En aquest cas és fàcil de veure que totes tres variables aporten per igual. També es veu com els gols marcats per l’equip local i els marcats per l’equip visitant no apunten cap a la mateixa direcció, però tampoc en direccions oposades. Això vol que estan una mica correlacionades. També veiem que totes dos estan bastant correlacionades amb els gols totals en un partit.
Utilitzem la funció prcomp per fer una anàlisi de les
components principals de la taula:
player_attributes_pca <- prcomp(df_player_attributes_nor, center = TRUE, scale. = TRUE)
summary(player_attributes_pca)
## Importance of components:
## PC1 PC2 PC3 PC4 PC5 PC6 PC7
## Standard deviation 4.0191 2.4067 1.98281 1.57964 1.41944 1.19891 1.05730
## Proportion of Variance 0.3846 0.1379 0.09361 0.05941 0.04797 0.03422 0.02662
## Cumulative Proportion 0.3846 0.5225 0.61611 0.67552 0.72349 0.75772 0.78433
## PC8 PC9 PC10 PC11 PC12 PC13 PC14
## Standard deviation 0.99847 0.98639 0.86410 0.81385 0.74289 0.67930 0.64533
## Proportion of Variance 0.02374 0.02317 0.01778 0.01577 0.01314 0.01099 0.00992
## Cumulative Proportion 0.80807 0.83124 0.84901 0.86478 0.87792 0.88891 0.89883
## PC15 PC16 PC17 PC18 PC19 PC20 PC21
## Standard deviation 0.5903 0.5798 0.55980 0.53885 0.53160 0.52293 0.49469
## Proportion of Variance 0.0083 0.0080 0.00746 0.00691 0.00673 0.00651 0.00583
## Cumulative Proportion 0.9071 0.9151 0.92259 0.92950 0.93623 0.94274 0.94857
## PC22 PC23 PC24 PC25 PC26 PC27 PC28
## Standard deviation 0.47293 0.45757 0.42917 0.41658 0.41298 0.39733 0.38596
## Proportion of Variance 0.00533 0.00498 0.00439 0.00413 0.00406 0.00376 0.00355
## Cumulative Proportion 0.95389 0.95888 0.96326 0.96740 0.97146 0.97521 0.97876
## PC29 PC30 PC31 PC32 PC33 PC34 PC35
## Standard deviation 0.36523 0.35904 0.32290 0.30496 0.28930 0.28678 0.24001
## Proportion of Variance 0.00318 0.00307 0.00248 0.00221 0.00199 0.00196 0.00137
## Cumulative Proportion 0.98194 0.98501 0.98749 0.98970 0.99170 0.99365 0.99503
## PC36 PC37 PC38 PC39 PC40 PC41
## Standard deviation 0.23441 0.21836 0.19792 0.18400 0.17922 0.03355
## Proportion of Variance 0.00131 0.00114 0.00093 0.00081 0.00076 0.00003
## Cumulative Proportion 0.99633 0.99747 0.99840 0.99921 0.99997 1.00000
## PC42
## Standard deviation 1.174e-14
## Proportion of Variance 0.000e+00
## Cumulative Proportion 1.000e+00
En aquest cas obtenim moltes components principals més, en concret n’hi ha 42. Però veiem que la primera ja explica un 0,3846 de la variabilitat del total de dades, i que a partir del tretzè component s’explica menys de 0,1099.
Per poder fer servir el mètode de Kàiser hem de calcular la variància dels components principals a partir de la desviació estàndard:
var_acc <- player_attributes_pca$sdev^2
var_acc
## [1] 1.615303e+01 5.792055e+00 3.931530e+00 2.495269e+00 2.014816e+00
## [6] 1.437382e+00 1.117893e+00 9.969422e-01 9.729734e-01 7.466722e-01
## [11] 6.623531e-01 5.518809e-01 4.614503e-01 4.164451e-01 3.485069e-01
## [16] 3.361424e-01 3.133754e-01 2.903616e-01 2.825983e-01 2.734522e-01
## [21] 2.447165e-01 2.236618e-01 2.093691e-01 1.841890e-01 1.735410e-01
## [26] 1.705494e-01 1.578678e-01 1.489674e-01 1.333914e-01 1.289109e-01
## [31] 1.042646e-01 9.300136e-02 8.369512e-02 8.224358e-02 5.760332e-02
## [36] 5.494848e-02 4.768119e-02 3.917153e-02 3.385595e-02 3.212029e-02
## [41] 1.125377e-03 1.377781e-28
var_acc > 1
## [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
## [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [37] FALSE FALSE FALSE FALSE FALSE FALSE
Seguint aquest mètode, escollim els components PCA1, PCA2, PCA3, PCA4, PCA5 i PCA6.
La funció get_pca_var(...) ens retorna un objecte que es
pot fer servir per seleccionar els elements principals que hem triat. En
aquest cas mostrem les coordenades que ens serveixen per Generació del
un diagrama de dispersió:
player_attributes_var <- get_pca_var(player_attributes_pca)
head(player_attributes_var$coord[, 1:6])
## Dim.1 Dim.2 Dim.3 Dim.4 Dim.5
## overall_rating 0.4229777 0.12231348 0.70154747 -0.20918070 0.31384678
## potential 0.3691020 -0.02888391 0.46076640 -0.28350469 0.55860812
## crossing 0.8559132 -0.03440580 0.01983916 -0.13515477 -0.12933918
## finishing 0.7364945 -0.42436345 0.20199393 0.31906648 0.01653091
## heading_accuracy 0.5825595 0.49970965 0.04051559 0.37161005 0.14425648
## short_passing 0.9002185 0.14413285 0.05158948 -0.04138471 -0.03765355
## Dim.6
## overall_rating -0.12057789
## potential -0.25137418
## crossing -0.07907206
## finishing 0.01246492
## heading_accuracy 0.11890413
## short_passing -0.18543781
A continuació, mostrem un histograma per veure el pes total dels components sobre el conjunt de dades:
ev <- get_eig(player_attributes_pca)
ev
## eigenvalue variance.percent cumulative.variance.percent
## Dim.1 1.615303e+01 3.845958e+01 38.45958
## Dim.2 5.792055e+00 1.379061e+01 52.25019
## Dim.3 3.931530e+00 9.360786e+00 61.61098
## Dim.4 2.495269e+00 5.941117e+00 67.55209
## Dim.5 2.014816e+00 4.797182e+00 72.34928
## Dim.6 1.437382e+00 3.422339e+00 75.77162
## Dim.7 1.117893e+00 2.661649e+00 78.43326
## Dim.8 9.969422e-01 2.373672e+00 80.80694
## Dim.9 9.729734e-01 2.316603e+00 83.12354
## Dim.10 7.466722e-01 1.777791e+00 84.90133
## Dim.11 6.623531e-01 1.577031e+00 86.47836
## Dim.12 5.518809e-01 1.314002e+00 87.79236
## Dim.13 4.614503e-01 1.098691e+00 88.89105
## Dim.14 4.164451e-01 9.915359e-01 89.88259
## Dim.15 3.485069e-01 8.297782e-01 90.71237
## Dim.16 3.361424e-01 8.003389e-01 91.51271
## Dim.17 3.133754e-01 7.461319e-01 92.25884
## Dim.18 2.903616e-01 6.913372e-01 92.95018
## Dim.19 2.825983e-01 6.728531e-01 93.62303
## Dim.20 2.734522e-01 6.510767e-01 94.27411
## Dim.21 2.447165e-01 5.826583e-01 94.85677
## Dim.22 2.236618e-01 5.325282e-01 95.38929
## Dim.23 2.093691e-01 4.984978e-01 95.88779
## Dim.24 1.841890e-01 4.385451e-01 96.32634
## Dim.25 1.735410e-01 4.131928e-01 96.73953
## Dim.26 1.705494e-01 4.060699e-01 97.14560
## Dim.27 1.578678e-01 3.758758e-01 97.52147
## Dim.28 1.489674e-01 3.546843e-01 97.87616
## Dim.29 1.333914e-01 3.175986e-01 98.19376
## Dim.30 1.289109e-01 3.069306e-01 98.50069
## Dim.31 1.042646e-01 2.482492e-01 98.74894
## Dim.32 9.300136e-02 2.214318e-01 98.97037
## Dim.33 8.369512e-02 1.992741e-01 99.16964
## Dim.34 8.224358e-02 1.958180e-01 99.36546
## Dim.35 5.760332e-02 1.371508e-01 99.50261
## Dim.36 5.494848e-02 1.308297e-01 99.63344
## Dim.37 4.768119e-02 1.135266e-01 99.74697
## Dim.38 3.917153e-02 9.326554e-02 99.84023
## Dim.39 3.385595e-02 8.060940e-02 99.92084
## Dim.40 3.212029e-02 7.647687e-02 99.99732
## Dim.41 1.125377e-03 2.679469e-03 100.00000
## Dim.42 1.377781e-28 3.280430e-28 100.00000
fviz_eig(player_attributes_pca)
També podem visualitzar la qualitat de representació de les variables
en el mapa de factors. Ho fem accedint a la variable cos2
de l’objecte que hem creat prèviament.
corrplot(player_attributes_var$cos2[, 1:6], is.corr = FALSE)
fviz_cos2(player_attributes_pca, choice = "var", axes = 1:2)
En aquesta taula amb tantes variables és més complex que es puguin
veure representades amb només 2 dimensions. Veiem que la que té un valor
cos2 més alt és ball_control.
Finalment, també visualitzem la contribució de les variables PCA fent
servir la funció fviz_pca_var:
fviz_pca_var(player_attributes_pca,
col.var = "contrib",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE
)
Aquí podem veure que les variables que més contribueixen són
ball_control, standing_tackle i
dribbling entre algunes altres que també contribueixen
molt. Les que menys són les variables que tenen a veure amb la data de
naixement. També veiem que més o menys totes van en la mateixa direcció,
excepte els atributs de porter. També veiem com el pes i l’alçada no
estan gens relacionades amb l’agilitat.
Utilitzem la funció prcomp per fer una anàlisi de les
components principals de la taula:
team_attributes_pca <- prcomp(df_team_attributes_nor, center = TRUE, scale. = TRUE)
summary(team_attributes_pca)
## Importance of components:
## PC1 PC2 PC3 PC4 PC5 PC6 PC7
## Standard deviation 1.3390 1.2839 1.0826 1.0468 0.92843 0.85986 0.80526
## Proportion of Variance 0.1992 0.1832 0.1302 0.1218 0.09578 0.08215 0.07205
## Cumulative Proportion 0.1992 0.3824 0.5126 0.6344 0.73014 0.81229 0.88434
## PC8 PC9
## Standard deviation 0.76611 0.67382
## Proportion of Variance 0.06521 0.05045
## Cumulative Proportion 0.94955 1.00000
En aquesta taula hem aconseguit 9 components principals. Veiem com la proporció de la variabilitat total està molt més repartida. El primer component només n’explica 0,1992, i a partir d’aquí, la resta en van tenint menys.
Per poder fer servir el mètode de Kàiser hem de calcular la variància dels components principals a partir de la desviació estàndard:
var_acc <- team_attributes_pca$sdev^2
var_acc
## [1] 1.7929480 1.6483964 1.1720999 1.0958055 0.8619778 0.7393665 0.6484499
## [8] 0.5869280 0.4540279
var_acc > 1
## [1] TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE
Seguint aquest mètode, escollim els components PCA1, PCA2, PCA3 i PCA4.
La funció get_pca_var(...) ens retorna un objecte que es
pot fer servir per seleccionar els elements principals que hem triat. En
aquest cas mostrem les coordenades que ens serveixen per Generació del
un diagrama de dispersió:
team_attributes_var <- get_pca_var(player_attributes_pca)
head(team_attributes_var$coord[, 1:4])
## Dim.1 Dim.2 Dim.3 Dim.4
## overall_rating 0.4229777 0.12231348 0.70154747 -0.20918070
## potential 0.3691020 -0.02888391 0.46076640 -0.28350469
## crossing 0.8559132 -0.03440580 0.01983916 -0.13515477
## finishing 0.7364945 -0.42436345 0.20199393 0.31906648
## heading_accuracy 0.5825595 0.49970965 0.04051559 0.37161005
## short_passing 0.9002185 0.14413285 0.05158948 -0.04138471
A continuació, mostrem un histograma per veure el pes total dels components sobre el conjunt de dades:
ev <- get_eig(team_attributes_pca)
ev
## eigenvalue variance.percent cumulative.variance.percent
## Dim.1 1.7929480 19.921644 19.92164
## Dim.2 1.6483964 18.315515 38.23716
## Dim.3 1.1720999 13.023333 51.26049
## Dim.4 1.0958055 12.175617 63.43611
## Dim.5 0.8619778 9.577531 73.01364
## Dim.6 0.7393665 8.215183 81.22882
## Dim.7 0.6484499 7.204999 88.43382
## Dim.8 0.5869280 6.521423 94.95525
## Dim.9 0.4540279 5.044755 100.00000
fviz_eig(team_attributes_pca)
També podem visualitzar la qualitat de representació de les variables
en el mapa de factors. Ho fem accedint a la variable cos2
de l’objecte que hem creat prèviament:
corrplot(team_attributes_var$cos2[, 1:4], is.corr = FALSE)
fviz_cos2(team_attributes_pca, choice = "var", axes = 1:2)
En aquesta taula ja no hi ha tantes variables, però també hem
obtingut valors cos2 relativament baixos. L’única variable
que destaca amb un valor alt és defencePressure.
Finalment, també visualitzem la contribució de les variables PCA fent
servir la funció fviz_pca_var:
fviz_pca_var(team_attributes_pca,
col.var = "contrib",
gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
repel = TRUE
)
En aquest gràfic es pot observar que la variable que més contribueix
amb diferència és defencePressure. I les que menys
chanceCreationShooting i buildUpPlayDribbling.
En general, com és lògic, les variables del mateix grup tenen estan
correlacionades.
Un cop hem fet l’anàlisi i tractament previ de les dades, ja podem aplicar models de Machine Learning per a obtenir més coneixement sobre les dades.
Les taules que ens poden ser útils en aquest treball són les que contenen la informació bàsica modificada i les taules que han estat normalitzades:
df_leaguedf_matchdf_player_attributesdf_team_attributesdf_match_nordf_player_attributes_nordf_team_norEl primer que farem és aplicar un model no supervisat basat en el concepte de distància. En aquest cas fem servir K-means.
L’objectiu que tenim amb l’aplicació d’aquest algorisme és la d’aconseguir agrupar els jugadors segons les seues característiques.
En el futbol, s’acostuma a classificar els jugadors en quatre grans grups segons la posició del camp on juguen. Hi ha porters, defenses, migcampistes i davanters. Dins d’aquestes categories encara es poden fer més divisions. A més, hi ha molts matisos, ja que, depèn de l’estil de joc d’un equip les característiques dels jugadors hauran de ser diferents, encara que juguin en la mateixa posició. També s’ha de tenir en compte que un jugador pot ser versàtil i, per tant, pertànyer a diferents categories segons el partit.
El que esperem d’aquesta anàlisi és veure com hi ha un grup clarament diferenciat de la resta, els porters. I volem veure com s’agrupen la resta de jugadors i si s’observen les categories esmentades anteriorment.
Primer de tot, carreguem els paquets necessaris:
packages <- c("cluster", "fpc")
not_installed <- packages[!(packages %in% installed.packages())]
if (length(not_installed) > 0) {
install.packages(not_installed, repos = "http:/cran.us.r-project.org")
}
lapply(packages, library, character.only = TRUE)
## [[1]]
## [1] "cluster" "corrplot" "factoextra" "xfun" "dplyr"
## [6] "Rmisc" "plyr" "lattice" "ggplot2" "RSQLite"
## [11] "stats" "graphics" "grDevices" "utils" "datasets"
## [16] "methods" "base"
##
## [[2]]
## [1] "fpc" "cluster" "corrplot" "factoextra" "xfun"
## [6] "dplyr" "Rmisc" "plyr" "lattice" "ggplot2"
## [11] "RSQLite" "stats" "graphics" "grDevices" "utils"
## [16] "datasets" "methods" "base"
Ara ja podem implementar el model. Cal escollir el nombre de grups
k en els que volem dividir les dades. Depenent de la
k aconseguirem una qualitat del model més o menys bona. Com
que no coneixem quina és la millor k el que farem serà
provar diversos valors.
En aquesta taula hi ha múltiples valors per a cada jugador, això es deu al fet que es tenen registres dels futbolistes en anys diferents. No volem tenir diversos registres per a cada un, així que ens quedem amb el seus atributs més recents:
df_player_attributes$date <- as.Date(df_player_attributes$date, "%Y-%m-%d")
# https://stackoverflow.com/questions/30058708/select-row-with-most-recent-date-by-group
df_player_clustering <- df_player_attributes[
tapply(1:nrow(df_player_attributes),
df_player_attributes$player_api_id,
function(ii) ii[which.max(df_player_attributes$date[ii])]), ]
No tots els atributs són útils a l’hora de realitzar l’agrupament. Així que ens quedem amb els que volem fer servir:
remove_attr <- c(
"player_api_id",
"player_fifa_api_id",
"id.x",
"date",
"id.y",
"player_name",
"birthday"
)
df_player_clustering <- select(df_player_clustering, !all_of(remove_attr))
Convertim les variables categòriques a factor:
df_player_clustering[sapply(df_player_clustering, is.character)] <- lapply(
df_player_clustering[sapply(df_player_clustering, is.character)],
as.factor
)
df_player_clustering[sapply(df_player_clustering, is.factor)] <-
data.matrix(df_player_clustering[sapply(df_player_clustering, is.factor)])
Aquest model no és compatible amb valors NA,
NaN o Inf, així que els traiem:
df_player_clustering <-
df_player_clustering[is.finite(rowSums(df_player_clustering)), ]
Quan es treballa amb aquest algorisme, és millor estandaritzar les dades:
df_player_clustering <- as.data.frame(scale(df_player_clustering))
Ara ja podem aplicar l’algorisme K-means amb. Inicialment fem servir 4 agrupacions:
player_kmeans4 <- kmeans(df_player_clustering, 4)
y_cluster4 <- player_kmeans4$cluster
I finalment visualitzem els clústers fent servir la funció
clusplot:
clusplot(df_player_clustering, y_cluster4,
color = TRUE, shade = TRUE, labels = 4, lines = 0)
És difícil distingir subgrups dins del grup més gran. La tria inicial
del valor de grups a dividir no sembla gens bona, ja que, molts
registres sembla que poden pertànyer a qualsevol altre clúster.
Malgrat tot, la funció cusplot(...) mostra les dades amb
els dos primers components principals del PCA. En aquest cas, veiem que
només s’explica el 49,32 % de la variabilitat, així que podria ser que
estiguessin ben classificats.
Comprovarem diverses mesures de qualitat. A continuació mostrem quines són:
Provem quin és el millor nombre d’agrupacions per a aquestes dades:
d <- daisy(df_player_clustering)
k_values <- 2:30
results_1 <- data.frame(k = k_values,
silhouette = double(length(k_values)),
withinss = double(length(k_values)))
for (i in k_values) {
fit <- kmeans(df_player_clustering, i)
y_cluster <- fit$cluster
sk <- silhouette(y_cluster, dist(df_player_clustering))
results_1[i - 1, 2] <- mean(sk[, 3])
results_1[i - 1, 3] <- fit$tot.withinss
}
results_1
## k silhouette withinss
## 1 2 0.47565816 309818.4
## 2 3 0.19650567 257719.3
## 3 4 0.16717281 234636.6
## 4 5 0.15728781 222174.9
## 5 6 0.15161556 210538.6
## 6 7 0.13814698 203097.0
## 7 8 0.13735839 196381.7
## 8 9 0.13219652 192021.6
## 9 10 0.12844524 188040.8
## 10 11 0.12262052 185877.2
## 11 12 0.09066737 182692.3
## 12 13 0.08856499 179652.6
## 13 14 0.08505862 177424.1
## 14 15 0.08402146 175698.0
## 15 16 0.08202682 172970.0
## 16 17 0.11106797 171736.2
## 17 18 0.11136996 170086.8
## 18 19 0.10923424 168883.9
## 19 20 0.07690475 166774.2
## 20 21 0.10789721 165953.2
## 21 22 0.07446697 163961.4
## 22 23 0.10681950 164019.1
## 23 24 0.10456540 162800.9
## 24 25 0.07005768 160525.0
## 25 26 0.10415402 160761.5
## 26 27 0.07043926 158572.2
## 27 28 0.07159053 157657.1
## 28 29 0.10089204 158321.0
## 29 30 0.06590925 156443.8
Volem provar si obtenim millors resultats amb una versió reduïda del
data frame. Aquest cop, extraiem les característiques dels
jugadors que no tenen a veure clarament amb el seu estil de joc.
Suprimim les variables overall_rating,
potential, birth_year,
birth_month, bmi i age:
remove_attr <- c(
"overall_rating",
"potential",
"birth_year",
"birth_month",
"bmi",
"age"
)
df_player_clustering_rd <- select(df_player_clustering, !all_of(remove_attr))
d <- daisy(df_player_clustering_rd)
k_values <- 2:30
results1_2 <- data.frame(k = k_values,
silhouette = double(length(k_values)),
withinss = double(length(k_values)))
for (i in k_values) {
fit <- kmeans(df_player_clustering_rd, i)
y_cluster <- fit$cluster
sk <- silhouette(y_cluster, dist(df_player_clustering_rd))
results1_2[i - 1, 2] <- mean(sk[, 3])
results1_2[i - 1, 3] <- fit$tot.withinss
}
results1_2
## k silhouette withinss
## 1 2 0.51539822 250851.0
## 2 3 0.22922282 199966.9
## 3 4 0.19509164 177532.1
## 4 5 0.18804767 165806.1
## 5 6 0.17679831 158414.7
## 6 7 0.15420882 151544.4
## 7 8 0.14909339 147077.8
## 8 9 0.14755438 143545.0
## 9 10 0.14311977 140691.7
## 10 11 0.13704085 138140.3
## 11 12 0.12999790 136427.9
## 12 13 0.09161779 135807.9
## 13 14 0.09311572 136153.3
## 14 15 0.12868813 129907.2
## 15 16 0.08835007 129538.5
## 16 17 0.08525006 126904.5
## 17 18 0.08542205 125257.2
## 18 19 0.12192329 124710.2
## 19 20 0.08321761 123081.4
## 20 21 0.12172604 122713.7
## 21 22 0.07951338 123997.8
## 22 23 0.11831074 120120.6
## 23 24 0.08016209 119217.8
## 24 25 0.07729607 118473.5
## 25 26 0.07882176 117236.3
## 26 27 0.07814502 116485.2
## 27 28 0.07615547 117410.8
## 28 29 0.07689352 115588.9
## 29 30 0.07939160 114138.5
Els resultats són un pèl millors, així que ens quedem amb aquest data frame.
Veiem que amb la mesura Silhouette el millor resultat l’ha obtingut el clustering amb 2 grups, sembla lògic perquè a simple vista ja es veuen fàcilment dos grups. L’agrupació amb 3 grups ha sigut la segona que ha donat més bon resultat, però té un resultat bastant pobre en comparació amb els 2 grups.
Pel que fa a la mesura Withinss, la millor
k s’acostuma a escollir en el punt en què la corba de la
següent gràfica es comença a estabilitzar. En aquest cas seria
14.
plot(results1_2$k, results1_2$withinss,
type = "o", col = "blue", pch = 0,
xlab = "Nombre de clústers", ylab = "Withinss")
Una altra forma de trobar la millor k és utilitzar la
funció kmeansruns del paquet fpc que executa
l’algorisme K-means com un conjunt de valors i
selecciona el nombre de clústers que millor funcioni d’acord amb la
silueta mitjana (asw) i Calinski-Harabasz (ch).
fit_ch <- kmeansruns(df_player_clustering_rd,
krange = k_values,
criterion = "ch")
fit_asw <- kmeansruns(df_player_clustering_rd,
krange = k_values,
criterion = "asw")
print(fit_ch$bestk)
## [1] 2
print(fit_asw$bestk)
## [1] 2
El valor obtingut amb tots dos casos és 2.
Per finalitzar, mostrem la gràfica clusplot amb catorze
agrupacions:
player_kmeans14 <- kmeans(df_player_clustering_rd, 14)
y_cluster14 <- player_kmeans14$cluster
clusplot(df_player_clustering_rd, y_cluster14,
color = TRUE, shade = TRUE, labels = 4, lines = 0)
Hem pogut observar que generalment el millor nombre d’agrupacions per a dividir els jugadors basant-nos en les seues característiques és dos. Ja hem comentat diverses vegades que es deu a la clara diferència que hi ha entre els jugadors de camp i els porters.
També hem pogut veure que amb la mesura Withinss el millor nombre d’agrupacions és al voltant de catorze. Això, probablement es deu al fet que hi ha molts tipus de jugadors i, per tant, costa molt classificar-los en poques categories. Hem de pensar que hi ha 10 jugadors al camp, que desenvolupen un rol diferent. I a més els suplents, poden tenir altres funcions. En conseqüència, si a tot això li sumem el fet que certs jugadors amb característiques similars fan tasques diferents, ens trobem que costa molt de separar en clústers.
En aquest apartat aprofundim amb els algorismes de clustering. Aquest cop farem servir una mètrica de distància diferent i comparem els resultats obtinguts amb el model anterior.
Per defecte, la funció kmeans(...) de la llibreria
stats fa servir l’algorisme Hartigan and Wong
(1978) per defecte amb la mètrica de distància
euclidiana. En aquest cas, provarem amb la distància
Manhattan.
Comencem a generar el model. Aquest cop no ens serveix la funció
kmeans(...) de la llibreria stats per què no
ens permet canviar de mètrica de distància.
Fem servir la funció hclust(...) de la llibreria
stat. Aquesta funció s’utilitza per a generar un agrupament
jeràrquic. L’agrupament jeràrquic és un mètode d’agrupament de punts de
dades en grups (o clústers) basats en la seva similitud.
Aquesta funció crea un arbre anomenat dendrograma, que mostra les
relacions entre els punts de dades i els clústers als quals
pertanyen.
Primer, calculem la matriu de distàncies de les dades dels jugadors
utilitzant la funció dist(...) amb la mètrica de distància
manhattan. Després apliquem el model amb el mètode
ward.D, que és un mètode que intenta minimitzar la
variància entre les distàncies dels punts d’una agrupació a una
altra:
d_player_mh <- dist(df_player_clustering_rd, method = "manhattan")
player_mh <- hclust(d_player_mh, method = "ward.D")
plot(player_mh)
Podem observar que s’han generat dos grups principals ben diferenciats, però un d’ells conté moltes més subdivisions que l’altre. Podem deduir fàcilment que l’agrupament de més a l’esquerra és el dels porters i, l’altre és el dels jugadors de camp.
Tal com hem fet anteriorment, calculem la silhouette amb
diversos valors de k per a conèixer quin és el nombre òptim
d’agrupacions.
Primer de tot, calculem el punt de tall per obtenir diferents
clústers. Utilitzem la funció cutree(...) per obtenir
diferents clústers a partir del resultat de hclust(...):
Després, calculem el punt de silueta per cada nombre de grups:
k_values = 2:30
results_mh <- data.frame(k = k_values,
silhouette = double(length(k_values)))
for (i in k_values) {
y_cluster <- cutree(player_mh, k = i)
sk <- silhouette(y_cluster, d_player_mh)
results_mh[i - 1, 2] <- mean(sk[, 3])
}
results_mh
## k silhouette
## 1 2 0.57240484
## 2 3 0.23587290
## 3 4 0.19475414
## 4 5 0.16340985
## 5 6 0.15269248
## 6 7 0.14387257
## 7 8 0.13228295
## 8 9 0.12563787
## 9 10 0.11425126
## 10 11 0.10882982
## 11 12 0.06986312
## 12 13 0.06768430
## 13 14 0.06646533
## 14 15 0.05921678
## 15 16 0.05752081
## 16 17 0.05865500
## 17 18 0.05847554
## 18 19 0.05613726
## 19 20 0.05817578
## 20 21 0.05366450
## 21 22 0.05554523
## 22 23 0.05668727
## 23 24 0.05472497
## 24 25 0.05206734
## 25 26 0.05019943
## 26 27 0.04256667
## 27 28 0.04121230
## 28 29 0.04057813
## 29 30 0.04020356
Veiem com la k amb puntuació més alta torna a ser 2.
Això ens indica que la millor forma de dividir els jugadors amb les
dades que tenim és fent servir dos grups.
Els resultats molt similars als obtinguts amb
kmeans(...) amb la distància euclidiana. En tots dos casos
hem trobat que la millor k era 2. La principal diferència
que veiem entre tots dos mètodes és que el valor de la
silhouette és més alt si fem servir hclust(...)
amb distància manhattan.
Per concloure aquest apartat hem de comentar que ens ha semblat bastant difícil. No ha sigut fàcil trobar funcions i paquets d’R que facin clustering amb l’opció de modificar la mètrica de distància.
La primera aproximació ha estat intentar modificar l’algorisme
K-means, però utilitzant la distància manhattan. Això
ho hem arribat a aconseguir amb la funció kcca(...), però
després no hem sigut capaços ni de visualitzar els agrupaments ni de
fer-li una anàlisi de la qualitat.
Finalment, hem decidit canviar d’algorisme no supervisat i fer servir
la funció hclust(...) combinada amb la distància
manhattan. Aquesta funció ens permet canviar fàcilment de
mètrica de distància, però aquesta solució no ens ha acabat de convèncer
per què no estem fent servir el mateix algorisme que en la primera
secció.
Malgrat tot, hem aconseguit fer una agrupació amb millor resultat que
la del primer model. Per tant, valorem positivament el fet d’haver
utilitzat hclust(...).
Per acabar, cal reiterar que sembla que amb les dades disponibles és complicat agrupar els jugadors. Un dels problemes que ja hem comentat és que cada jugador pot jugar a diverses posicions i que jugadors amb característiques similars juguin en posicions diferents.
També podria ser que el fet d’estar avaluant a tots els jugadors alhora pugui donar problemes. És a dir, els jugadors que són dolents tindran valoracions molt baixes en tots els camps. Això vol dir que, un davanter dolent no tindrà gaires diferències de puntuació entre les habilitats que se li pressuposen a un atacant entre les que ha de tenir un defensor. En canvi, un jugador molt bo, tindrà aquestes diferències molt marcades. En definitiva, creiem que seria interessant repetir aquest experiment filtrant per nivell dels jugadors.
En aquest apartat seguim amb els models no supervisats. Però, aquest cop, fem servir dos algorismes de clustering basats en la densitat. Aquests s’especialitzen a identificar zones d’alta concentració d’observacions separades entre si per zones amb una menor densitat d’observacions.
El primer algorisme que apliquem és DBSCAN. Aquest necessita inicialment dos paràmetres:
minPts: Nombre mínim de
punts a l’\(\epsilon\)-veïnatge d’un
punt.DBSCAN construeix esferes de radi \(\epsilon\) que incloguin almenys
minPts punts.
Primer de tot, carreguem els paquets necessaris:
packages <- c("dbscan")
not_installed <- packages[!(packages %in% installed.packages())]
if (length(not_installed) > 0) {
install.packages(not_installed, repos = "http:/cran.us.r-project.org")
}
lapply(packages, library, character.only = TRUE)
## [[1]]
## [1] "dbscan" "fpc" "cluster" "corrplot" "factoextra"
## [6] "xfun" "dplyr" "Rmisc" "plyr" "lattice"
## [11] "ggplot2" "RSQLite" "stats" "graphics" "grDevices"
## [16] "utils" "datasets" "methods" "base"
Ara realitzem una cerca per obtenir els paràmetres que ens donin el millor model (tenint en compte el mètode Silhouette):
d <- daisy(df_player_clustering_rd)
eps_vec <- c(2, 2, 2, 2, 2,
5, 5, 5, 5, 5,
7, 7, 7, 7, 7,
10, 10, 10, 10, 10,
15, 15, 15, 15, 15,
20, 20, 20, 20, 20,
50, 50, 50, 50, 50)
min_pts_vec <- c(2, 3, 5, 10, 15, 20, 100,
2, 3, 5, 10, 15, 20, 100,
2, 3, 5, 10, 15, 20, 100,
2, 3, 5, 10, 15, 20, 100,
2, 3, 5, 10, 15, 20, 100)
results_dbscan <- data.frame(eps = eps_vec,
minPts = min_pts_vec,
silhouette = double(length(eps_vec)),
clusters = integer(length(eps_vec)))
for (i in 1:nrow(results_dbscan)) {
res <- dbscan(df_player_clustering_rd,
eps = eps_vec[i],
minPts = min_pts_vec[i])
sk <- silhouette(res$cluster, d)
value <- tryCatch(
{
val <- sk[, 3]
mean(val)
},
error = function(cond) {
return(NA)
})
results_dbscan[i, ]$silhouette <- value
results_dbscan[i, ]$clusters <- length(unique(res$cluster))
}
results_dbscan[order(results_dbscan$silhouette, decreasing = TRUE), ]
## eps minPts silhouette clusters
## 11 7 10 0.5153982 2
## 12 7 15 0.5153982 2
## 13 7 20 0.5153982 2
## 14 7 100 0.5153982 2
## 15 7 2 0.5153982 2
## 6 5 20 0.3569387 3
## 8 5 2 0.3569387 3
## 9 5 3 0.3569387 3
## 10 5 5 0.3569387 3
## 7 5 100 0.3292062 3
## 3 2 5 -0.3223893 6
## 2 2 3 -0.4715452 54
## 1 2 2 -0.5132098 215
## 4 2 10 NA 1
## 5 2 15 NA 1
## 16 10 3 NA 1
## 17 10 5 NA 1
## 18 10 10 NA 1
## 19 10 15 NA 1
## 20 10 20 NA 1
## 21 15 100 NA 1
## 22 15 2 NA 1
## 23 15 3 NA 1
## 24 15 5 NA 1
## 25 15 10 NA 1
## 26 20 15 NA 1
## 27 20 20 NA 1
## 28 20 100 NA 1
## 29 20 2 NA 1
## 30 20 3 NA 1
## 31 50 5 NA 1
## 32 50 10 NA 1
## 33 50 15 NA 1
## 34 50 20 NA 1
## 35 50 100 NA 1
results_dbscan <- na.omit(results_dbscan)
max_dbscan_eps <- results_dbscan[which.max(results_dbscan$silhouette), 1]
max_dbscan_pts <- results_dbscan[which.max(results_dbscan$silhouette), 2]
max_dbscan_sk <- max(results_dbscan$silhouette)
Podem observar que una de les millors combinacions és fent servir eps = 7 i minPts = 10, que obtenen una Silhouette de 0.5153982. Aquest resultat podria ser millor, això significa que l’agrupació no és de tot bona. Veiem també que els millors models fan la separació amb dos grups, i que els segons millors models ho fan amb tres agrupacions.
A continuació, ens guardem un dels models amb millor resultat:
players_dbscan <- dbscan(df_player_clustering_rd,
eps = max_dbscan_eps,
minPts = max_dbscan_pts)
players_dbscan
## DBSCAN clustering for 9902 objects.
## Parameters: eps = 7, minPts = 10
## Using euclidean distances and borderpoints = TRUE
## The clustering contains 2 cluster(s) and 0 noise points.
##
## 1 2
## 9034 868
##
## Available fields: cluster, eps, minPts, dist, borderPoints
L’algorisme OPTICS serveix per trobar clústers basats en la densitat en dades espacials. La idea bàsica és similar a DBSCAN, però soluciona un dels seus principals problemes, que és haver de detectar agrupacions en dades amb densitat variable. Es pot entendre com a una generalització de DBSCAN.
El que fa és assignar una distància d’assolibilitat a cada punt del
dataset. Necessita un radi \(\epsilon\) i un criteri de densitat
minPts, però a diferència de DBSCAN el valor del radi no
determina la formació de clústers, sinó que serveix per reduir la
complexitat de càlcul.
players_optics <- optics(df_player_clustering_rd)
players_optics
## OPTICS ordering/clustering for 9902 objects.
## Parameters: minPts = 5, eps = 6.06827883934815, eps_cl = NA, xi = NA
## Available fields: order, reachdist, coredist, predecessor, minPts, eps,
## eps_cl, xi
També es poden extraure agrupacions similars a DBSCAN. La funció
optics(...) admet que se li especifiqui el paràmetre
eps_cl, que és el llindar per identificar els
clústers (eps_cl \(\leq\) eps).
eps_vec <- c(.1, 10, 40, 80, 100, 300, 1000,
5000, 10000, 50000, 100000, 1000000)
results_optics <- data.frame(eps_cl = eps_vec,
silhouette = double(length(eps_vec)),
clusters = integer(length(eps_vec)))
for (i in 1:length(eps_vec)) {
res <- optics(df_player_clustering_rd)
res <- extractDBSCAN(res, eps_cl = eps_vec[i])
sk <- silhouette(res$cluster, d)
value <- tryCatch(
{
val <- sk[, 3]
mean(val)
},
error = function(cond) {
return(NA)
})
results_optics[i, ]$silhouette <- value
results_optics[i, ]$clusters <- length(unique(res$cluster))
}
results_optics[order(results_optics$silhouette, decreasing = TRUE), ]
## eps_cl silhouette clusters
## 2 1e+01 0.5153982 2
## 3 4e+01 0.5153982 2
## 4 8e+01 0.5153982 2
## 5 1e+02 0.5153982 2
## 6 3e+02 0.5153982 2
## 7 1e+03 0.5153982 2
## 8 5e+03 0.5153982 2
## 9 1e+04 0.5153982 2
## 10 5e+04 0.5153982 2
## 11 1e+05 0.5153982 2
## 12 1e+06 0.5153982 2
## 1 1e-01 NA 1
results_optics <- na.omit(results_optics)
max_optics_eps <- results_optics[which.max(results_optics$silhouette), 1]
max_optics_clt <- results_optics[which.max(results_optics$silhouette), 2]
max_optics_sk <- max(results_optics$silhouette)
Podem comprovar que els resultats són molt similars als de DBSCAN. En
tots dos casos la millor Silhouette s’assoleix
quan es fan servir dos clústers, a més el seu valor és igual
0.5153982. En la majoria de proves s’ha aconseguit el millor model, un
dels valors de eps_cl amb els quals s’ha obtingut la màxima
puntuació és 10.
Ens guardem un dels millors models:
res <- optics(df_player_clustering_rd)
players_optics <- extractDBSCAN(res, eps_cl = max_optics_eps)
players_optics
## OPTICS ordering/clustering for 9902 objects.
## Parameters: minPts = 5, eps = 6.06827883934815, eps_cl = 10, xi = NA
## The clustering contains 2 cluster(s) and 0 noise points.
##
## 1 2
## 9034 868
##
## Available fields: order, reachdist, coredist, predecessor, minPts, eps,
## eps_cl, xi, cluster
A continuació visualitzem el model creat. Un diagrama d’accessibilitat o reachability plot és un gràfic que mostra de forma visual la distància d’accessibilitat de cada punt. Les valls representen clústers i els cims indiquen els punts que estan entre les agrupacions. Com més profunda és la vall, més dens és el clúster. Els punts entre agrupacions possiblement són outliers.
plot(players_optics)
Els models obtinguts amb aquests algorismes són iguals als generats amb l’algorisme \(K\)-means. A més, els resultats obtinguts no són gaire satisfactoris perquè només aconseguim dividir els jugadors en dos categories, els porters i els jugadors de camp. És probable que amb les dades que tenim actualment no sigui fàcil diferenciar clarament entre els tipus de jugadors de camp.
En l’exercici anterior no s’havia tingut en compte aquest cas d’ús. Per això, ara cal preparar les dades adequadament per a poder implementar-ho.
El primer que fem és seleccionar els equips de forma única. És a dir, no volem un registre per a cada any, sinó un únic registre per a cada equip:
df_team_attributes$date <- as.Date(df_team_attributes$date, "%Y-%m-%d")
# https://stackoverflow.com/questions/30058708/select-row-with-most-recent-date-by-group
df_team_tree <-
group_by(df_team_attributes, team_api_id) %>%
slice(which.max(date))
df_team_tree <- as.data.frame(df_team_tree)
Després calculem el nombre de partits i victòries de cada equip i ho
guardem al data frame df_team_tree:
calculate_home_games <- function(team_id, df_matches) {
return(length(which(df_matches$home_team_api_id == team_id)))
}
calculate_away_games <- function(team_id, df_matches) {
return(length(which(df_matches$away_team_api_id == team_id)))
}
calculate_home_wins <- function(team_id, df_matches) {
df_home_wins <- which(df_matches$home_team_api_id == team_id &
df_matches$home_team_goal > df_matches$home_team_goal)
home_wins <- length(df_home_wins)
return(home_wins)
}
calculate_away_wins <- function(team_id, df_matches) {
df_away_wins <- which(df_matches$away_team_api_id == team_id &
df_matches$away_team_goal > df_matches$home_team_goal)
away_wins <- length(df_away_wins)
return(away_wins)
}
df_team_tree$home_matches <- sapply(df_team_tree$team_api_id,
function(i) calculate_home_games(i, df_match))
df_team_tree$away_matches <- sapply(df_team_tree$team_api_id,
function(i) calculate_away_games(i, df_match))
df_team_tree$matches <- df_team_tree$home_matches + df_team_tree$away_matches
df_team_tree$home_wins <- sapply(df_team_tree$team_api_id,
function(i) calculate_home_wins(i, df_match))
df_team_tree$home_wins_p <- df_team_tree$home_wins / df_team_tree$home_matches
df_team_tree$away_wins <- sapply(df_team_tree$team_api_id,
function(i) calculate_away_wins(i, df_match))
df_team_tree$away_wins_p <- df_team_tree$away_wins / df_team_tree$away_matches
df_team_tree$wins <- df_team_tree$home_wins + df_team_tree$away_wins
Cada equip consta de molts atributs, alguns d’ells són similars. Seleccionem els que considerem rellevants i treiem la resta. Alguns d’ells són identificadors que no aporten res al model i la resta són les variables numèriques. Aquestes les traiem per què amb les variables categòriques contenen la mateixa informació de forma simplificada:
keep_attr <- c(
"team_api_id",
"buildUpPlaySpeedClass",
"buildUpPlayDribblingClass",
"buildUpPlayPassingClass",
"buildUpPlayPositioningClass",
"chanceCreationPassingClass",
"chanceCreationCrossingClass",
"chanceCreationShootingClass",
"chanceCreationPositioningClass",
"defencePressureClass",
"defenceAggressionClass",
"defenceTeamWidthClass",
"defenceDefenderLineClass",
"matches",
"home_wins_p",
"away_wins_p",
"wins"
)
df_team_tree <- select(df_team_tree, all_of(keep_attr))
També cal crear la variable booleana
local_win:
df_match$local_win <- df_match$home_team_goal > df_match$away_team_goal
Com que els jugadors que han participat en cada partit no ens interessen, esborrem les columnes que contenen els seus identificadors:
keep_attr <- c(
"id",
"league_id",
"season",
"stage",
"home_team_api_id",
"away_team_api_id",
"local_win"
)
df_match_tree <- select(df_match, all_of(keep_attr))
Ara ja podem fer una unió de les característiques de cada equip amb
el data frame match:
df_match_tree <- merge(x = df_match_tree, y = df_team_tree,
by.x = "home_team_api_id", by.y = "team_api_id")
# Rename new columns to add the prefix "home_team_"
names(df_match_tree) <- c("home_team_api_id",
"id",
"league_id",
"season",
"stage",
"away_team_api_id",
"local_win",
"home_team_buildUpPlaySpeedClass",
"home_team_buildUpPlayDribblingClass",
"home_team_buildUpPlayPassingClass",
"home_team_buildUpPlayPositioningClass",
"home_team_chanceCreationPassingClass",
"home_team_chanceCreationCrossingClass",
"home_team_chanceCreationShootingClass",
"home_team_chanceCreationPositioningClass",
"home_team_defencePressureClass",
"home_team_defenceAggressionClass",
"home_team_defenceTeamWidthClass",
"home_team_defenceDefenderLineClass",
"home_team_matches",
"home_team_home_wins_p",
"home_team_away_wins_p",
"home_team_wins")
df_match_tree <- merge(x = df_match_tree, y = df_team_tree,
by.x = "away_team_api_id", by.y = "team_api_id")
# Rename new columns to add the prefix "away_team_"
names(df_match_tree) <- c("home_team_api_id",
"id",
"league_id",
"season",
"stage",
"away_team_api_id",
"local_win",
"home_team_buildUpPlaySpeedClass",
"home_team_buildUpPlayDribblingClass",
"home_team_buildUpPlayPassingClass",
"home_team_buildUpPlayPositioningClass",
"home_team_chanceCreationPassingClass",
"home_team_chanceCreationCrossingClass",
"home_team_chanceCreationShootingClass",
"home_team_chanceCreationPositioningClass",
"home_team_defencePressureClass",
"home_team_defenceAggressionClass",
"home_team_defenceTeamWidthClass",
"home_team_defenceDefenderLineClass",
"home_team_matches",
"home_team_home_wins_p",
"home_team_away_wins_p",
"home_team_wins",
"away_team_buildUpPlaySpeedClass",
"away_team_buildUpPlayDribblingClass",
"away_team_buildUpPlayPassingClass",
"away_team_buildUpPlayPositioningClass",
"away_team_chanceCreationPassingClass",
"away_team_chanceCreationCrossingClass",
"away_team_chanceCreationShootingClass",
"away_team_chanceCreationPositioningClass",
"away_team_defencePressureClass",
"away_team_defenceAggressionClass",
"away_team_defenceTeamWidthClass",
"away_team_defenceDefenderLineClass",
"away_team_matches",
"away_team_home_wins_p",
"away_team_away_wins_p",
"away_team_wins")
Per poder utilitzar el data frame amb l’arbre C5.0 convertim
totes les variables categòriques a factors:
df_match_tree[sapply(df_match_tree, is.character)] <- lapply(
df_match_tree[sapply(df_match_tree, is.character)],
as.factor
)
df_match_tree$local_win <- as.factor(df_match_tree$local_win)
str(df_match_tree)
## 'data.frame': 25629 obs. of 39 variables:
## $ home_team_api_id : int 1601 1601 1601 1601 1601 1601 1601 1601 1601 1601 ...
## $ id : int 8030 8028 2186 8673 2186 2183 8245 8030 8673 8030 ...
## $ league_id : int 15803 17470 17415 16304 17187 16911 16769 17590 15909 16987 ...
## $ season : int 15722 15722 15722 15722 15722 15722 15722 15722 15722 15722 ...
## $ stage : Factor w/ 8 levels "2008/2009","2009/2010",..: 1 8 8 3 7 5 5 8 1 6 ...
## $ away_team_api_id : int 19 17 10 20 12 8 19 30 30 17 ...
## $ local_win : Factor w/ 2 levels "FALSE","TRUE": 2 2 2 1 2 2 2 2 2 1 ...
## $ home_team_buildUpPlaySpeedClass : Factor w/ 3 levels "Balanced","Fast",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ home_team_buildUpPlayDribblingClass : Factor w/ 3 levels "Little","Lots",..: 3 3 3 3 3 1 2 3 3 3 ...
## $ home_team_buildUpPlayPassingClass : Factor w/ 3 levels "Long","Mixed",..: 2 1 2 2 2 2 2 2 2 2 ...
## $ home_team_buildUpPlayPositioningClass : Factor w/ 2 levels "Free Form","Organised": 1 2 2 2 2 2 2 1 2 1 ...
## $ home_team_chanceCreationPassingClass : Factor w/ 3 levels "Normal","Risky",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ home_team_chanceCreationCrossingClass : Factor w/ 3 levels "Little","Lots",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ home_team_chanceCreationShootingClass : Factor w/ 3 levels "Little","Lots",..: 3 3 1 3 1 3 3 3 3 3 ...
## $ home_team_chanceCreationPositioningClass: Factor w/ 2 levels "Free Form","Organised": 2 2 2 1 2 2 2 2 1 2 ...
## $ home_team_defencePressureClass : Factor w/ 3 levels "Deep","High",..: 3 3 3 3 3 3 1 3 3 3 ...
## $ home_team_defenceAggressionClass : Factor w/ 3 levels "Contain","Double",..: 3 3 3 3 3 1 3 3 3 3 ...
## $ home_team_defenceTeamWidthClass : Factor w/ 3 levels "Narrow","Normal",..: 2 2 2 2 2 2 2 2 2 2 ...
## $ home_team_defenceDefenderLineClass : Factor w/ 2 levels "Cover","Offside Trap": 1 1 1 1 1 1 1 1 1 1 ...
## $ home_team_matches : int 240 180 210 240 210 150 210 240 240 240 ...
## $ home_team_home_wins_p : num 0 0 0 0 0 0 0 0 0 0 ...
## $ home_team_away_wins_p : num 0.258 0.3 0.181 0.467 0.181 ...
## $ home_team_wins : int 31 27 19 56 19 26 26 31 56 31 ...
## $ away_team_buildUpPlaySpeedClass : Factor w/ 3 levels "Balanced","Fast",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ away_team_buildUpPlayDribblingClass : Factor w/ 3 levels "Little","Lots",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ away_team_buildUpPlayPassingClass : Factor w/ 3 levels "Long","Mixed",..: 2 2 2 2 2 2 2 2 2 2 ...
## $ away_team_buildUpPlayPositioningClass : Factor w/ 2 levels "Free Form","Organised": 2 2 2 2 2 2 2 2 2 2 ...
## $ away_team_chanceCreationPassingClass : Factor w/ 3 levels "Normal","Risky",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ away_team_chanceCreationCrossingClass : Factor w/ 3 levels "Little","Lots",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ away_team_chanceCreationShootingClass : Factor w/ 3 levels "Little","Lots",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ away_team_chanceCreationPositioningClass: Factor w/ 2 levels "Free Form","Organised": 2 2 2 2 2 2 2 2 2 2 ...
## $ away_team_defencePressureClass : Factor w/ 3 levels "Deep","High",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ away_team_defenceAggressionClass : Factor w/ 3 levels "Contain","Double",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ away_team_defenceTeamWidthClass : Factor w/ 3 levels "Narrow","Normal",..: 2 2 2 2 2 2 2 2 2 2 ...
## $ away_team_defenceDefenderLineClass : Factor w/ 2 levels "Cover","Offside Trap": 1 1 1 1 1 1 1 1 1 1 ...
## $ away_team_matches : int 240 240 240 240 240 240 240 240 240 240 ...
## $ away_team_home_wins_p : num 0 0 0 0 0 0 0 0 0 0 ...
## $ away_team_away_wins_p : num 0.317 0.317 0.317 0.317 0.317 ...
## $ away_team_wins : int 38 38 38 38 38 38 38 38 38 38 ...
Finalment, generem els conjunts de dades d’entrenament i de proves per a poder entrenar i estudiar la qualitat de l’arbre creat:
y <- df_match_tree$local_win
# Remove target attribute, plus unnecessary attributes
remove_attr <- c("local_win", "home_team_api_id", "id", "season")
X <- select(df_match_tree, !all_of(remove_attr))
set.seed(1899)
split_prop <- 3
indexes <- sample(1:nrow(df_match_tree),
size = floor(((split_prop - 1) / split_prop) * nrow(df_match_tree))
)
train_X <- X[indexes, ]
train_y <- y[indexes]
test_X <- X[-indexes, ]
test_y <- y[-indexes]
Ara podem procedir a entrenar el model.
El primer que hem de fer és carregar la llibreria C50
per a generar l’arbre i les llibreries grid i
ggplot2 per a visualitzar-lo:
packages <- c("C50", "grid", "ggplot2")
not_installed <- packages[!(packages %in% installed.packages())]
if (length(not_installed) > 0) {
install.packages(not_installed, repos = "http:/cran.us.r-project.org")
}
lapply(packages, library, character.only = TRUE)
## [[1]]
## [1] "C50" "dbscan" "fpc" "cluster" "corrplot"
## [6] "factoextra" "xfun" "dplyr" "Rmisc" "plyr"
## [11] "lattice" "ggplot2" "RSQLite" "stats" "graphics"
## [16] "grDevices" "utils" "datasets" "methods" "base"
##
## [[2]]
## [1] "grid" "C50" "dbscan" "fpc" "cluster"
## [6] "corrplot" "factoextra" "xfun" "dplyr" "Rmisc"
## [11] "plyr" "lattice" "ggplot2" "RSQLite" "stats"
## [16] "graphics" "grDevices" "utils" "datasets" "methods"
## [21] "base"
##
## [[3]]
## [1] "grid" "C50" "dbscan" "fpc" "cluster"
## [6] "corrplot" "factoextra" "xfun" "dplyr" "Rmisc"
## [11] "plyr" "lattice" "ggplot2" "RSQLite" "stats"
## [16] "graphics" "grDevices" "utils" "datasets" "methods"
## [21] "base"
Ara creem el model utilitzant les dades d’entrenament. Fem servir
l’argument trials per a fer servir adaptative
boosting, que consisteix a agregar les prediccions de múltiples
predictors per a construir diversos arbres de decisió i que aquests
decideixin quina és la millor classe per a cada registre:
c50_model <- C5.0(train_X, train_y, trials = 100)
Fem servir la funció summary(...) perquè ens retorni
informació sobre el model que acabem de crear. Mostra la crida que l’ha
creat, el nombre de registres i atributs que s’han fet servir, les
regles dels diferents arbres que ha generat i fa una petita avaluació
del model amb les dades d’entrenament.
summary(c50_model)
##
## Call:
## C5.0.default(x = train_X, y = train_y, trials = 100)
##
##
## C5.0 [Release 2.07 GPL Edition] Tue Jan 24 21:44:26 2023
## -------------------------------
##
## Class specified by attribute `outcome'
##
## Read 17086 cases (36 attributes) from undefined.data
##
## ----- Trial 0: -----
##
## Decision tree:
##
## home_team_away_wins_p > 0.368421:
## :...home_team_buildUpPlayPassingClass = Long: FALSE (13/2)
## : home_team_buildUpPlayPassingClass in {Mixed,Short}:
## : :...away_team_wins <= 41: TRUE (2537/681)
## : away_team_wins > 41:
## : :...home_team_away_wins_p > 0.5384616:
## : :...away_team_away_wins_p <= 0.6447368: TRUE (225/49)
## : : away_team_away_wins_p > 0.6447368: FALSE (27/9)
## : home_team_away_wins_p <= 0.5384616:
## : :...away_team_away_wins_p > 0.5367647: FALSE (99/22)
## : away_team_away_wins_p <= 0.5367647:
## : :...stage in {2008/2009,2012/2013,
## : : 2015/2016}: FALSE (270/123)
## : stage in {2010/2011,2011/2012}: TRUE (173/75)
## : stage = 2009/2010: [S1]
## : stage = 2013/2014: [S2]
## : stage = 2014/2015: [S3]
## home_team_away_wins_p <= 0.368421:
## :...away_team_away_wins_p > 0.3464052: FALSE (3491/848)
## away_team_away_wins_p <= 0.3464052:
## :...away_team_away_wins_p > 0.2315789:
## :...home_team_wins <= 41: FALSE (4059/1536)
## : home_team_wins > 41:
## : :...stage in {2010/2011,2013/2014,2014/2015}: TRUE (360/159)
## : stage = 2008/2009: [S4]
## : stage = 2011/2012:
## : :...away_team_defenceTeamWidthClass in {Narrow,
## : : : Wide}: TRUE (6)
## : : away_team_defenceTeamWidthClass = Normal: FALSE (89/40)
## : stage = 2012/2013: [S5]
## : stage = 2015/2016: [S6]
## : stage = 2009/2010:
## : :...home_team_defencePressureClass in {Deep,
## : : High}: TRUE (5)
## : home_team_defencePressureClass = Medium: [S7]
## away_team_away_wins_p <= 0.2315789:
## :...home_team_wins > 33: TRUE (1586/629)
## home_team_wins <= 33:
## :...away_team_away_wins_p <= 0.1555556: TRUE (805/352)
## away_team_away_wins_p > 0.1555556:
## :...away_team_buildUpPlaySpeedClass = Slow: TRUE (13/4)
## away_team_buildUpPlaySpeedClass = Fast: [S8]
## away_team_buildUpPlaySpeedClass = Balanced:
## :...home_team_chanceCreationShootingClass = Little: [S9]
## home_team_chanceCreationShootingClass = Lots: [S10]
## home_team_chanceCreationShootingClass = Normal: [S11]
##
## SubTree [S1]
##
## home_team_defenceDefenderLineClass = Cover: TRUE (82/39)
## home_team_defenceDefenderLineClass = Offside Trap: FALSE (5)
##
## SubTree [S2]
##
## home_team_buildUpPlaySpeedClass in {Balanced,Slow}: FALSE (69/25)
## home_team_buildUpPlaySpeedClass = Fast: TRUE (14/4)
##
## SubTree [S3]
##
## home_team_chanceCreationShootingClass = Little: TRUE (7)
## home_team_chanceCreationShootingClass in {Lots,Normal}:
## :...away_team_chanceCreationShootingClass in {Little,Lots}: FALSE (20/4)
## away_team_chanceCreationShootingClass = Normal: TRUE (59/26)
##
## SubTree [S4]
##
## home_team_chanceCreationPassingClass = Normal: FALSE (71/32)
## home_team_chanceCreationPassingClass in {Risky,Safe}: TRUE (14/2)
##
## SubTree [S5]
##
## away_team_buildUpPlayDribblingClass in {Little,Lots}: TRUE (6)
## away_team_buildUpPlayDribblingClass = Normal: FALSE (109/49)
##
## SubTree [S6]
##
## home_team_chanceCreationCrossingClass in {Little,Normal}: FALSE (116/46)
## home_team_chanceCreationCrossingClass = Lots: TRUE (14/4)
##
## SubTree [S7]
##
## away_team_buildUpPlaySpeedClass = Balanced: FALSE (90/40)
## away_team_buildUpPlaySpeedClass in {Fast,Slow}: TRUE (6)
##
## SubTree [S8]
##
## away_team_defenceAggressionClass = Contain: TRUE (5/1)
## away_team_defenceAggressionClass = Double: FALSE (5)
## away_team_defenceAggressionClass = Press:
## :...home_team_chanceCreationPassingClass in {Normal,Safe}: FALSE (144/49)
## home_team_chanceCreationPassingClass = Risky: TRUE (19/7)
##
## SubTree [S9]
##
## away_team_buildUpPlayPassingClass in {Long,Short}: FALSE (17/5)
## away_team_buildUpPlayPassingClass = Mixed: TRUE (152/65)
##
## SubTree [S10]
##
## away_team_chanceCreationCrossingClass in {Little,Lots}: FALSE (5)
## away_team_chanceCreationCrossingClass = Normal:
## :...away_team_api_id <= 35: TRUE (72/29)
## away_team_api_id > 35: FALSE (8)
##
## SubTree [S11]
##
## home_team_chanceCreationPositioningClass = Free Form: TRUE (118/53)
## home_team_chanceCreationPositioningClass = Organised:
## :...home_team_matches <= 180: FALSE (966/388)
## home_team_matches > 180:
## :...stage = 2008/2009: TRUE (152/68)
## stage in {2010/2011,2011/2012,2012/2013,2013/2014,
## : 2014/2015}: FALSE (735/351)
## stage = 2009/2010:
## :...away_team_defenceTeamWidthClass in {Narrow,
## : : Normal}: FALSE (131/56)
## : away_team_defenceTeamWidthClass = Wide: TRUE (4)
## stage = 2015/2016:
## :...away_team_defenceDefenderLineClass = Offside Trap: FALSE (5)
## away_team_defenceDefenderLineClass = Cover:
## :...home_team_buildUpPlaySpeedClass in {Fast,
## : Slow}: TRUE (7)
## home_team_buildUpPlaySpeedClass = Balanced:
## :...away_team_matches <= 218: FALSE (56/13)
## away_team_matches > 218: TRUE (45/17)
##
## ----- Trial 1: -----
##
## Decision tree:
##
## away_team_away_wins_p > 0.3636364: FALSE (3599.2/1177)
## away_team_away_wins_p <= 0.3636364:
## :...home_team_away_wins_p > 0.4055944: TRUE (2498.9/826.5)
## home_team_away_wins_p <= 0.4055944:
## :...home_team_wins <= 9:
## :...away_team_matches > 238: FALSE (666.9/207.1)
## : away_team_matches <= 238:
## : :...away_team_buildUpPlayDribblingClass = Little: TRUE (133.3/54.8)
## : away_team_buildUpPlayDribblingClass in {Lots,
## : Normal}: FALSE (616.6/277.3)
## home_team_wins > 9:
## :...away_team_wins > 41: FALSE (1476.1/644.2)
## away_team_wins <= 41:
## :...home_team_away_wins_p > 0.3358209: TRUE (1030/436.3)
## home_team_away_wins_p <= 0.3358209:
## :...away_team_buildUpPlaySpeedClass = Fast: FALSE (452.7/209.3)
## away_team_buildUpPlaySpeedClass = Slow: TRUE (187.9/81.9)
## away_team_buildUpPlaySpeedClass = Balanced: [S1]
##
## SubTree [S1]
##
## home_team_chanceCreationCrossingClass in {Little,Lots}: TRUE (643.9/295)
## home_team_chanceCreationCrossingClass = Normal:
## :...home_team_buildUpPlayPassingClass = Long: FALSE (135.7/54.3)
## home_team_buildUpPlayPassingClass = Short: TRUE (70.8/34.5)
## home_team_buildUpPlayPassingClass = Mixed:
## :...stage in {2009/2010,2010/2011,2013/2014,2014/2015}: TRUE (2823.4/1378)
## stage in {2012/2013,2015/2016}: FALSE (1423.3/671.9)
## stage = 2008/2009:
## :...home_team_buildUpPlaySpeedClass in {Balanced,
## : : Slow}: FALSE (599.8/286.5)
## : home_team_buildUpPlaySpeedClass = Fast: TRUE (24.1/6.3)
## stage = 2011/2012:
## :...home_team_defenceTeamWidthClass in {Narrow,
## : Normal}: FALSE (679.7/330.6)
## home_team_defenceTeamWidthClass = Wide: TRUE (23.6/5.3)
##
## ----- Trial 2: -----
##
## Decision tree:
##
## away_team_away_wins_p > 0.4528302: FALSE (1548.8/444.9)
## away_team_away_wins_p <= 0.4528302:
## :...home_team_wins > 69: TRUE (1202.2/362.7)
## home_team_wins <= 69:
## :...home_team_matches <= 210: FALSE (4862.7/2114.3)
## home_team_matches > 210:
## :...away_team_away_wins_p <= 0.1832061: TRUE (2011/859.9)
## away_team_away_wins_p > 0.1832061:
## :...home_team_buildUpPlayPassingClass in {Long,
## : Short}: FALSE (533.2/237.2)
## home_team_buildUpPlayPassingClass = Mixed: [S1]
##
## SubTree [S1]
##
## away_team_buildUpPlayDribblingClass in {Little,Lots}: FALSE (843.5/387.7)
## away_team_buildUpPlayDribblingClass = Normal:
## :...stage in {2008/2009,2009/2010,2011/2012,2015/2016}: TRUE (2921.4/1373.4)
## stage in {2010/2011,2013/2014,2014/2015}: FALSE (2334.9/1116.5)
## stage = 2012/2013:
## :...away_team_wins <= 58: TRUE (791.7/377.9)
## away_team_wins > 58: FALSE (36.6/8.1)
##
## ----- Trial 3: -----
##
## Decision tree:
##
## home_team_away_wins_p > 0.493421: TRUE (1234.1/404.7)
## home_team_away_wins_p <= 0.493421:
## :...away_team_wins > 64: FALSE (1344.2/410.7)
## away_team_wins <= 64:
## :...away_team_away_wins_p > 0.2735849:
## :...home_team_matches <= 111: FALSE (529.9/164)
## : home_team_matches > 111:
## : :...home_team_buildUpPlayPassingClass in {Long,
## : : Short}: FALSE (352.4/138.9)
## : home_team_buildUpPlayPassingClass = Mixed: [S1]
## away_team_away_wins_p <= 0.2735849:
## :...home_team_wins > 46: TRUE (1823.4/777.5)
## home_team_wins <= 46:
## :...away_team_defenceAggressionClass = Contain: TRUE (328.5/145.1)
## away_team_defenceAggressionClass = Double: FALSE (37.7/15.4)
## away_team_defenceAggressionClass = Press:
## :...stage in {2008/2009,2013/2014}: TRUE (1683/820.8)
## stage in {2009/2010,2010/2011,2014/2015,
## : 2015/2016}: FALSE (3420.3/1656.4)
## stage = 2011/2012: [S2]
## stage = 2012/2013: [S3]
##
## SubTree [S1]
##
## home_team_chanceCreationPassingClass in {Normal,Safe}: FALSE (4157.6/1923)
## home_team_chanceCreationPassingClass = Risky: TRUE (409.4/188.6)
##
## SubTree [S2]
##
## home_team_buildUpPlayDribblingClass in {Little,Lots}: FALSE (115.6/48.6)
## home_team_buildUpPlayDribblingClass = Normal: TRUE (749.7/338)
##
## SubTree [S3]
##
## away_team_buildUpPlaySpeedClass = Balanced: TRUE (803.8/380.7)
## away_team_buildUpPlaySpeedClass in {Fast,Slow}: FALSE (96.6/35.1)
##
## ----- Trial 4: -----
##
## Decision tree:
##
## away_team_away_wins_p > 0.4666667:
## :...home_team_wins <= 73: FALSE (1336.3/425.7)
## : home_team_wins > 73: TRUE (70.4/29)
## away_team_away_wins_p <= 0.4666667:
## :...home_team_away_wins_p > 0.3358209: TRUE (4179.9/1804.2)
## home_team_away_wins_p <= 0.3358209:
## :...away_team_away_wins_p > 0.3464052: FALSE (1910.1/828.3)
## away_team_away_wins_p <= 0.3464052:
## :...away_team_matches <= 70: TRUE (579.8/253.3)
## away_team_matches > 70:
## :...stage in {2008/2009,2011/2012,2012/2013}: FALSE (3375.3/1632.4)
## stage = 2010/2011: TRUE (1132.2/545.2)
## stage = 2009/2010:
## :...home_team_buildUpPlaySpeedClass = Balanced: TRUE (1049.2/519.6)
## : home_team_buildUpPlaySpeedClass in {Fast,
## : Slow}: FALSE (115.9/40)
## stage = 2013/2014: [S1]
## stage = 2014/2015: [S2]
## stage = 2015/2016:
## :...home_team_defencePressureClass = Deep: FALSE (80.8/31.1)
## home_team_defencePressureClass in {High,
## Medium}: TRUE (980.8/470)
##
## SubTree [S1]
##
## home_team_buildUpPlayDribblingClass in {Little,Lots}: TRUE (106.9/40.4)
## home_team_buildUpPlayDribblingClass = Normal: FALSE (1027.8/464.7)
##
## SubTree [S2]
##
## away_team_chanceCreationCrossingClass in {Little,Lots}: FALSE (82.3/29.3)
## away_team_chanceCreationCrossingClass = Normal: TRUE (1058.4/494.1)
##
## ----- Trial 5: -----
##
## Decision tree:
##
## away_team_wins > 76: FALSE (692.3/208.5)
## away_team_wins <= 76:
## :...away_team_away_wins_p > 0.4210526:
## :...home_team_away_wins_p <= 0.3636364: FALSE (964.3/371.5)
## : home_team_away_wins_p > 0.3636364: TRUE (263.7/119.4)
## away_team_away_wins_p <= 0.4210526:
## :...home_team_away_wins_p > 0.4485294: TRUE (1498.2/588.3)
## home_team_away_wins_p <= 0.4485294:
## :...home_team_buildUpPlayPassingClass = Long:
## :...home_team_chanceCreationShootingClass = Little: TRUE (101.8/41.8)
## : home_team_chanceCreationShootingClass in {Lots,
## : Normal}: FALSE (370.1/152.2)
## home_team_buildUpPlayPassingClass = Short:
## :...home_team_buildUpPlayPositioningClass = Free Form: TRUE (73.8/26.3)
## : home_team_buildUpPlayPositioningClass = Organised: FALSE (402.3/185.4)
## home_team_buildUpPlayPassingClass = Mixed:
## :...away_team_chanceCreationCrossingClass in {Little,
## : Lots}: FALSE (1365.7/656.9)
## away_team_chanceCreationCrossingClass = Normal:
## :...stage in {2008/2009,2011/2012,
## : 2012/2013}: TRUE (4172.6/2040.6)
## stage in {2009/2010,2013/2014,
## : 2015/2016}: FALSE (4227.3/2053)
## stage = 2010/2011:
## :...home_team_matches <= 190: FALSE (400.7/172)
## : home_team_matches > 190: TRUE (1045.9/491.6)
## stage = 2014/2015: [S1]
##
## SubTree [S1]
##
## home_team_buildUpPlayDribblingClass in {Little,Lots}: FALSE (189/72.1)
## home_team_buildUpPlayDribblingClass = Normal: TRUE (1318.3/638.3)
##
## ----- Trial 6: -----
##
## Decision tree:
##
## home_team_away_wins_p > 0.5735294: TRUE (506.4/161.9)
## home_team_away_wins_p <= 0.5735294:
## :...away_team_away_wins_p > 0.5073529: FALSE (874.5/278.4)
## away_team_away_wins_p <= 0.5073529:
## :...home_team_wins <= 11:
## :...away_team_wins <= 27: TRUE (1001.3/468.6)
## : away_team_wins > 27:
## : :...stage = 2013/2014: TRUE (107.7/46.9)
## : stage in {2008/2009,2009/2010,2010/2011,2011/2012,2012/2013,
## : : 2014/2015,2015/2016}:
## : :...home_team_matches <= 182: FALSE (1002.3/349.7)
## : home_team_matches > 182: TRUE (33.2/10.7)
## home_team_wins > 11:
## :...stage in {2008/2009,2013/2014,2014/2015}: TRUE (5009.6/2476.5)
## stage = 2012/2013: FALSE (1781.2/848.4)
## stage = 2009/2010:
## :...away_team_buildUpPlayPassingClass in {Long,
## : : Short}: TRUE (123.7/43.5)
## : away_team_buildUpPlayPassingClass = Mixed: FALSE (1541.6/761)
## stage = 2011/2012:
## :...away_team_defenceAggressionClass in {Contain,
## : : Press}: TRUE (1640.7/807.8)
## : away_team_defenceAggressionClass = Double: FALSE (12.1)
## stage = 2015/2016:
## :...home_team_matches <= 182: TRUE (299/117.4)
## : home_team_matches > 182: FALSE (1439.1/713.9)
## stage = 2010/2011:
## :...away_team_defenceDefenderLineClass = Offside Trap: FALSE (48/16)
## away_team_defenceDefenderLineClass = Cover:
## :...home_team_buildUpPlayPassingClass = Long: TRUE (18.8/5.8)
## home_team_buildUpPlayPassingClass = Short: FALSE (97.2/33.5)
## home_team_buildUpPlayPassingClass = Mixed: [S1]
##
## SubTree [S1]
##
## away_team_buildUpPlayPositioningClass = Free Form: TRUE (72.2/23.9)
## away_team_buildUpPlayPositioningClass = Organised:
## :...home_team_chanceCreationPositioningClass = Free Form: TRUE (136.7/54.5)
## home_team_chanceCreationPositioningClass = Organised:
## :...away_team_chanceCreationShootingClass = Little: TRUE (101.7/40.5)
## away_team_chanceCreationShootingClass in {Lots,
## Normal}: FALSE (1238.9/597.4)
##
## ----- Trial 7: -----
##
## Decision tree:
##
## away_team_wins > 76: FALSE (669.2/220.4)
## away_team_wins <= 76:
## :...home_team_away_wins_p > 0.5735294: TRUE (469.9/151.7)
## home_team_away_wins_p <= 0.5735294:
## :...home_team_wins <= 13:
## :...away_team_wins <= 27: TRUE (1193.9/584.6)
## : away_team_wins > 27: FALSE (1399/549.5)
## home_team_wins > 13:
## :...stage in {2008/2009,2011/2012,2012/2013,2013/2014,
## : 2014/2015}: FALSE (8336.4/4063.4)
## stage = 2009/2010:
## :...home_team_chanceCreationShootingClass in {Little,
## : : Normal}: TRUE (1503.3/704.2)
## : home_team_chanceCreationShootingClass = Lots: FALSE (132.2/53)
## stage = 2010/2011:
## :...home_team_buildUpPlayPassingClass in {Long,
## : : Mixed}: TRUE (1595.5/768.3)
## : home_team_buildUpPlayPassingClass = Short: FALSE (102.5/36.6)
## stage = 2015/2016:
## :...away_team_chanceCreationShootingClass = Little: FALSE (138/56.2)
## away_team_chanceCreationShootingClass in {Lots,
## Normal}: TRUE (1546.1/718.2)
##
## ----- Trial 8: -----
##
## Decision tree:
##
## home_team_wins > 75: TRUE (808.2/302.9)
## home_team_wins <= 75:
## :...away_team_away_wins_p > 0.5073529: FALSE (852.4/295)
## away_team_away_wins_p <= 0.5073529:
## :...home_team_wins <= 13: FALSE (2536.7/1140.8)
## home_team_wins > 13:
## :...home_team_chanceCreationShootingClass in {Little,
## : Normal}: TRUE (11971.4/5916)
## home_team_chanceCreationShootingClass = Lots: FALSE (917.3/441.1)
##
## ----- Trial 9: -----
##
## Decision tree:
##
## home_team_wins > 75: TRUE (801.5/308.8)
## home_team_wins <= 75:
## :...away_team_wins <= 10: TRUE (2166.4/1029)
## away_team_wins > 10: FALSE (14118.1/6688)
##
## ----- Trial 10: -----
##
## Decision tree:
##
## home_team_away_wins_p > 0.5241935: TRUE (896.5/356.2)
## home_team_away_wins_p <= 0.5241935:
## :...home_team_wins <= 42: FALSE (11798.8/5661.9)
## home_team_wins > 42:
## :...away_team_chanceCreationPositioningClass = Free Form: TRUE (478.9/209.8)
## away_team_chanceCreationPositioningClass = Organised:
## :...stage in {2008/2009,2009/2010,2012/2013,2013/2014,2014/2015,
## : 2015/2016}: TRUE (2937.2/1375.4)
## stage in {2010/2011,2011/2012}: FALSE (974.6/458.4)
##
## ----- Trial 11: -----
##
## Decision tree:
##
## away_team_away_wins_p > 0.4666667: FALSE (1315.5/521.2)
## away_team_away_wins_p <= 0.4666667:
## :...away_team_defenceAggressionClass = Contain: TRUE (493.8/232.8)
## away_team_defenceAggressionClass = Double: FALSE (128.7/57.7)
## away_team_defenceAggressionClass = Press:
## :...home_team_buildUpPlayPassingClass = Short: TRUE (731.7/342.1)
## home_team_buildUpPlayPassingClass = Long:
## :...home_team_chanceCreationShootingClass = Little: TRUE (109.8/44.8)
## : home_team_chanceCreationShootingClass in {Lots,
## : Normal}: FALSE (367.6/160.4)
## home_team_buildUpPlayPassingClass = Mixed:
## :...away_team_chanceCreationPassingClass in {Normal,
## : Risky}: TRUE (13572/6589.5)
## away_team_chanceCreationPassingClass = Safe: FALSE (366.9/172.3)
##
## ----- Trial 12: -----
##
## Decision tree:
##
## home_team_away_wins_p > 0.6447368: TRUE (315.5/112.1)
## home_team_away_wins_p <= 0.6447368:
## :...away_team_wins > 41: FALSE (5159.1/2364.7)
## away_team_wins <= 41:
## :...stage in {2008/2009,2010/2011,2014/2015}: FALSE (4371/2160.1)
## stage in {2009/2010,2011/2012,2012/2013,
## : 2013/2014}: TRUE (5792.5/2795.8)
## stage = 2015/2016:
## :...home_team_buildUpPlaySpeedClass in {Balanced,
## : Slow}: TRUE (1348.1/667.4)
## home_team_buildUpPlaySpeedClass = Fast: FALSE (99.9/39.8)
##
## ----- Trial 13: -----
##
## Decision tree:
##
## away_team_away_wins_p > 0.56: FALSE (520.8/187.4)
## away_team_away_wins_p <= 0.56:
## :...home_team_away_wins_p > 0.5735294: TRUE (437.2/162.6)
## home_team_away_wins_p <= 0.5735294:
## :...home_team_matches <= 150: FALSE (2618.5/1221.5)
## home_team_matches > 150:
## :...away_team_buildUpPlaySpeedClass in {Fast,
## : Slow}: TRUE (1411.7/673.4)
## away_team_buildUpPlaySpeedClass = Balanced:
## :...away_team_buildUpPlayPassingClass in {Long,
## : Short}: TRUE (918.6/438.2)
## away_team_buildUpPlayPassingClass = Mixed: FALSE (11179.2/5564.4)
##
## ----- Trial 14: -----
##
## Decision tree:
##
## away_team_away_wins_p > 0.4666667: FALSE (1293.3/538.4)
## away_team_away_wins_p <= 0.4666667:
## :...home_team_away_wins_p > 0.5241935: TRUE (822.9/336.2)
## home_team_away_wins_p <= 0.5241935:
## :...stage in {2008/2009,2010/2011,2012/2013}: TRUE (5658.9/2763.1)
## stage in {2009/2010,2011/2012,2015/2016}: FALSE (5590.7/2773.7)
## stage = 2013/2014:
## :...away_team_buildUpPlayDribblingClass in {Little,
## : : Lots}: FALSE (226.5/95.6)
## : away_team_buildUpPlayDribblingClass = Normal: TRUE (1556.8/751.3)
## stage = 2014/2015:
## :...away_team_buildUpPlayDribblingClass = Little: FALSE (167.9/69.4)
## away_team_buildUpPlayDribblingClass = Lots: TRUE (96.2/43.5)
## away_team_buildUpPlayDribblingClass = Normal:
## :...home_team_buildUpPlayPositioningClass = Free Form: TRUE (67.1/23.3)
## home_team_buildUpPlayPositioningClass = Organised: [S1]
##
## SubTree [S1]
##
## away_team_chanceCreationPositioningClass = Free Form: FALSE (137.9/54.1)
## away_team_chanceCreationPositioningClass = Organised:
## :...away_team_buildUpPlayPassingClass = Long: FALSE (62.7/23.1)
## away_team_buildUpPlayPassingClass = Short: TRUE (33.9/15)
## away_team_buildUpPlayPassingClass = Mixed:
## :...home_team_away_wins_p <= 0.2222222: FALSE (463.1/205)
## home_team_away_wins_p > 0.2222222: TRUE (908/407)
##
## ----- Trial 15: -----
##
## Decision tree:
##
## away_team_away_wins_p > 0.56: FALSE (512.3/192.5)
## away_team_away_wins_p <= 0.56:
## :...home_team_buildUpPlayPositioningClass = Free Form: TRUE (769/344.9)
## home_team_buildUpPlayPositioningClass = Organised:
## :...home_team_wins > 63: TRUE (1498.2/683.5)
## home_team_wins <= 63:
## :...home_team_buildUpPlayDribblingClass = Lots: TRUE (541.9/270.6)
## home_team_buildUpPlayDribblingClass = Little:
## :...away_team_defencePressureClass in {Deep,
## : : High}: TRUE (150.5/61.7)
## : away_team_defencePressureClass = Medium: FALSE (1372/660.6)
## home_team_buildUpPlayDribblingClass = Normal:
## :...home_team_chanceCreationCrossingClass in {Little,
## : Lots}: TRUE (1236.3/600.2)
## home_team_chanceCreationCrossingClass = Normal:
## :...stage in {2008/2009,2009/2010,
## : 2011/2012}: TRUE (3879.1/1891.9)
## stage in {2010/2011,2012/2013,2013/2014,2014/2015,
## 2015/2016}: FALSE (7126.7/3464.1)
##
## ----- Trial 16: -----
##
## Decision tree:
##
## away_team_wins > 73: FALSE (801.5/331.5)
## away_team_wins <= 73:
## :...home_team_away_wins_p > 0.6447368: TRUE (276.7/101.8)
## home_team_away_wins_p <= 0.6447368:
## :...away_team_away_wins_p <= 0.2516556:
## :...home_team_wins > 65: TRUE (409.5/159.9)
## : home_team_wins <= 65:
## : :...stage in {2008/2009,2010/2011,2012/2013,2013/2014,
## : : 2015/2016}: TRUE (4550.3/2198.7)
## : stage = 2011/2012: FALSE (922.2/450.8)
## : stage = 2014/2015:
## : :...away_team_buildUpPlayPassingClass = Long: FALSE (40.5/11.4)
## : : away_team_buildUpPlayPassingClass in {Mixed,
## : : Short}: TRUE (919.2/446.5)
## : stage = 2009/2010:
## : :...home_team_buildUpPlaySpeedClass = Slow: FALSE (24/5.7)
## : home_team_buildUpPlaySpeedClass in {Balanced,Fast}: [S1]
## away_team_away_wins_p > 0.2516556:
## :...home_team_wins <= 3: FALSE (157.7/49.1)
## home_team_wins > 3:
## :...stage in {2013/2014,2015/2016}: FALSE (2110.9/1034.4)
## stage = 2014/2015: TRUE (1041/512.1)
## stage = 2009/2010:
## :...away_team_defenceAggressionClass = Contain: TRUE (20.9/4.4)
## : away_team_defenceAggressionClass in {Double,
## : Press}: FALSE (926.7/434.4)
## stage = 2011/2012:
## :...away_team_defenceAggressionClass in {Contain,
## : : Press}: TRUE (976.2/469.3)
## : away_team_defenceAggressionClass = Double: FALSE (13.3/0.7)
## stage = 2012/2013:
## :...home_team_matches <= 272: FALSE (717.1/315.5)
## : home_team_matches > 272: TRUE (309.2/133.1)
## stage = 2008/2009:
## :...away_team_defenceAggressionClass = Contain: FALSE (21.5/6.3)
## : away_team_defenceAggressionClass = Double: TRUE (16.9/5.5)
## : away_team_defenceAggressionClass = Press: [S2]
## stage = 2010/2011:
## :...home_team_buildUpPlayPassingClass in {Long,
## : Short}: FALSE (72.4/23.4)
## home_team_buildUpPlayPassingClass = Mixed: [S3]
##
## SubTree [S1]
##
## away_team_defenceTeamWidthClass in {Narrow,Normal}: TRUE (914.5/421.1)
## away_team_defenceTeamWidthClass = Wide: FALSE (66.3/25.2)
##
## SubTree [S2]
##
## away_team_chanceCreationShootingClass in {Little,Normal}: FALSE (738.7/342.6)
## away_team_chanceCreationShootingClass = Lots: TRUE (125.2/47.5)
##
## SubTree [S3]
##
## away_team_chanceCreationPositioningClass = Free Form: TRUE (143.8/58.2)
## away_team_chanceCreationPositioningClass = Organised: FALSE (769.9/365.5)
##
## ----- Trial 17: -----
##
## Decision tree:
##
## away_team_away_wins_p > 0.56: FALSE (503/199.4)
## away_team_away_wins_p <= 0.56:
## :...home_team_wins > 69: TRUE (1131.3/499.5)
## home_team_wins <= 69:
## :...home_team_chanceCreationPassingClass = Risky: TRUE (1638.4/814.5)
## home_team_chanceCreationPassingClass = Safe: FALSE (404.4/184.6)
## home_team_chanceCreationPassingClass = Normal:
## :...home_team_buildUpPlaySpeedClass = Fast: FALSE (394.4/178.4)
## home_team_buildUpPlaySpeedClass = Slow: TRUE (282/132.4)
## home_team_buildUpPlaySpeedClass = Balanced:
## :...home_team_chanceCreationShootingClass = Little: TRUE (1076/515.3)
## home_team_chanceCreationShootingClass = Lots: FALSE (617.7/294.1)
## home_team_chanceCreationShootingClass = Normal:
## :...home_team_buildUpPlayPassingClass in {Long,
## : Short}: FALSE (348.3/144.9)
## home_team_buildUpPlayPassingClass = Mixed:
## :...away_team_wins > 64: FALSE (490.7/207.2)
## away_team_wins <= 64: [S1]
##
## SubTree [S1]
##
## away_team_chanceCreationShootingClass = Little: TRUE (779.4/387.2)
## away_team_chanceCreationShootingClass = Lots:
## :...home_team_matches <= 96: FALSE (39.5/10.4)
## : home_team_matches > 96: TRUE (472.1/212.2)
## away_team_chanceCreationShootingClass = Normal:
## :...stage in {2008/2009,2010/2011,2011/2012,2012/2013,
## : 2014/2015}: FALSE (5629.6/2775.3)
## stage = 2009/2010:
## :...home_team_chanceCreationCrossingClass in {Little,
## : : Normal}: TRUE (1033.2/488.7)
## : home_team_chanceCreationCrossingClass = Lots: FALSE (41.7/14.6)
## stage = 2013/2014:
## :...away_team_defenceDefenderLineClass = Offside Trap: FALSE (9/0.9)
## : away_team_defenceDefenderLineClass = Cover:
## : :...home_team_defenceAggressionClass = Contain: FALSE (24.4/6.8)
## : home_team_defenceAggressionClass in {Double,
## : Press}: TRUE (1042/505.6)
## stage = 2015/2016:
## :...home_team_chanceCreationCrossingClass = Little: FALSE (21.6/5.8)
## home_team_chanceCreationCrossingClass = Lots: TRUE (19.3/6.9)
## home_team_chanceCreationCrossingClass = Normal:
## :...home_team_away_wins_p <= 0.1578947: FALSE (70.8/19.7)
## home_team_away_wins_p > 0.1578947: TRUE (1017.2/481.6)
##
## ----- Trial 18: -----
##
## Decision tree:
##
## home_team_buildUpPlayPositioningClass = Free Form:
## :...home_team_matches <= 286: FALSE (562.3/276)
## : home_team_matches > 286: TRUE (225.7/82)
## home_team_buildUpPlayPositioningClass = Organised:
## :...away_team_buildUpPlayPositioningClass = Free Form: FALSE (706/325.5)
## away_team_buildUpPlayPositioningClass = Organised:
## :...home_team_buildUpPlayPassingClass in {Long,
## : Short}: FALSE (1132.8/541.3)
## home_team_buildUpPlayPassingClass = Mixed:
## :...away_team_api_id <= 4: FALSE (1618.8/744.7)
## away_team_api_id > 4:
## :...away_team_defenceAggressionClass = Contain: TRUE (394.8/181.6)
## away_team_defenceAggressionClass = Double: FALSE (166.2/78.4)
## away_team_defenceAggressionClass = Press:
## :...home_team_away_wins_p <= 0.2315789: FALSE (4885.9/2350.3)
## home_team_away_wins_p > 0.2315789:
## :...stage in {2009/2010,2011/2012,2012/2013,
## : 2014/2015}: TRUE (3733.8/1776.1)
## stage = 2013/2014: FALSE (920.5/447.3)
## stage = 2008/2009: [S1]
## stage = 2010/2011: [S2]
## stage = 2015/2016: [S3]
##
## SubTree [S1]
##
## home_team_chanceCreationShootingClass in {Little,Lots}: FALSE (168.4/70.5)
## home_team_chanceCreationShootingClass = Normal: TRUE (596.6/284.3)
##
## SubTree [S2]
##
## home_team_chanceCreationShootingClass = Little: FALSE (89.5/37.8)
## home_team_chanceCreationShootingClass = Lots: TRUE (70.2/24)
## home_team_chanceCreationShootingClass = Normal:
## :...away_team_defenceDefenderLineClass = Cover: TRUE (739.3/352.8)
## away_team_defenceDefenderLineClass = Offside Trap: FALSE (17.5/4.8)
##
## SubTree [S3]
##
## home_team_defenceDefenderLineClass = Offside Trap: TRUE (26.1/6.9)
## home_team_defenceDefenderLineClass = Cover:
## :...home_team_buildUpPlayDribblingClass in {Little,Normal}: TRUE (990.3/475.5)
## home_team_buildUpPlayDribblingClass = Lots: FALSE (41.2/11.7)
##
## ----- Trial 19: -----
##
## Decision tree:
##
## home_team_buildUpPlayPositioningClass = Free Form: TRUE (788.4/362.7)
## home_team_buildUpPlayPositioningClass = Organised:
## :...away_team_wins > 58:
## :...home_team_chanceCreationShootingClass in {Little,
## : : Lots}: TRUE (312.8/137.5)
## : home_team_chanceCreationShootingClass = Normal: FALSE (1810.7/799)
## away_team_wins <= 58:
## :...home_team_chanceCreationCrossingClass = Little: TRUE (378.4/177.1)
## home_team_chanceCreationCrossingClass = Lots: FALSE (1084.9/527.7)
## home_team_chanceCreationCrossingClass = Normal:
## :...away_team_chanceCreationPassingClass in {Normal,
## : Risky}: TRUE (12364.1/6123.9)
## away_team_chanceCreationPassingClass = Safe: FALSE (346.6/155.3)
##
## ----- Trial 20: -----
##
## Decision tree:
##
## home_team_away_wins_p <= 0.5241935: FALSE (16223.1/7960.6)
## home_team_away_wins_p > 0.5241935: TRUE (862.9/384.1)
##
## ----- Trial 21: -----
##
## Decision tree:
##
## home_team_buildUpPlayPositioningClass = Free Form: TRUE (787.3/365.3)
## home_team_buildUpPlayPositioningClass = Organised: FALSE (16298.7/8108.1)
##
## *** boosting reduced to 21 trials since last classifier is very inaccurate
##
##
## Evaluation on training data (17086 cases):
##
## Trial Decision Tree
## ----- ----------------
## Size Errors
##
## 0 50 5902(34.5%)
## 1 18 6408(37.5%)
## 2 10 6660(39.0%)
## 3 15 6522(38.2%)
## 4 15 6741(39.5%)
## 5 15 7164(41.9%)
## 6 21 7556(44.2%)
## 7 11 7567(44.3%)
## 8 5 7610(44.5%)
## 9 3 6867(40.2%)
## 10 5 6741(39.5%)
## 11 8 8043(47.1%)
## 12 6 7363(43.1%)
## 13 6 7598(44.5%)
## 14 14 7540(44.1%)
## 15 9 7260(42.5%)
## 16 26 7065(41.3%)
## 17 23 7461(43.7%)
## 18 19 7201(42.1%)
## 19 7 7975(46.7%)
## 20 2 7261(42.5%)
## boost 5877(34.4%) <<
##
##
## (a) (b) <-classified as
## ---- ----
## 6922 2304 (a): class FALSE
## 3573 4287 (b): class TRUE
##
##
## Attribute usage:
##
## 100.00% home_team_buildUpPlayPositioningClass
## 100.00% home_team_away_wins_p
## 100.00% home_team_wins
## 100.00% away_team_away_wins_p
## 100.00% away_team_wins
## 98.40% home_team_buildUpPlayPassingClass
## 96.98% away_team_defenceAggressionClass
## 95.89% stage
## 95.27% away_team_buildUpPlayPositioningClass
## 94.97% home_team_matches
## 94.13% home_team_chanceCreationShootingClass
## 89.85% home_team_chanceCreationCrossingClass
## 88.77% home_team_chanceCreationPassingClass
## 87.34% away_team_chanceCreationPassingClass
## 83.57% away_team_api_id
## 83.53% home_team_buildUpPlayDribblingClass
## 83.44% away_team_buildUpPlaySpeedClass
## 79.60% home_team_buildUpPlaySpeedClass
## 72.49% away_team_chanceCreationCrossingClass
## 72.33% away_team_buildUpPlayPassingClass
## 65.98% away_team_chanceCreationShootingClass
## 54.18% away_team_buildUpPlayDribblingClass
## 54.18% away_team_matches
## 35.96% away_team_chanceCreationPositioningClass
## 20.12% home_team_chanceCreationPositioningClass
## 16.91% away_team_defenceDefenderLineClass
## 9.04% away_team_defencePressureClass
## 6.62% home_team_defencePressureClass
## 6.51% home_team_defenceDefenderLineClass
## 6.08% away_team_defenceTeamWidthClass
## 5.96% home_team_defenceAggressionClass
## 4.00% home_team_defenceTeamWidthClass
##
##
## Time: 1.6 secs
Ara podem visualitzar el model. Per a fer-ho, hem de treure
l’argument rules:
c50_model <- C5.0(train_X, train_y, trials = 100)
plot(c50_model, gp = gpar(fontsize = 9.5))
Com que és un arbre amb moltes regles, es veu una imatge molt petita, però si hi cliquem amb el botó dret del ratolí i l’obrim en una pestanya nova es pot ampliar.
Una de les opcions és eliminar aquelles variables que aporten poc al model.
Per a fer-ho, utilitzem la funció anomenada C5imp(...)
que mostra la importància de cada atribut segons la mètrica escollida.
La mètrica usage es calcula la importància a partir del
percentatge de mostres del conjunt d’entrenament que acaben a un node
terminal després de la divisió. D’aquesta manera, tenim que la primera
variable a separar el conjunt té un valor de 100. A partir, d’aquesta,
la resta tenen valors més xics. la mètrica splits la
importància es calcula a partir del percentatge de separacions
associades a cada variable.
imp_usage <- C5imp(c50_model, metric = "usage")
imp_splits <- C5imp(c50_model, metric = "splits")
row_names <- sort(rownames(imp_usage))
imp_usage_sort <- imp_usage[order(rownames(imp_usage)), ]
imp_splits_sort <- imp_splits[order(rownames(imp_splits)), ]
df_imp <- data.frame(
attribute = row_names,
usage = imp_usage_sort,
splits = imp_splits_sort
)
df_imp
## attribute usage splits
## 1 away_team_api_id 83.57 0.9803922
## 2 away_team_away_wins_p 100.00 9.8039216
## 3 away_team_buildUpPlayDribblingClass 54.18 2.4509804
## 4 away_team_buildUpPlayPassingClass 72.33 2.4509804
## 5 away_team_buildUpPlayPositioningClass 95.27 0.9803922
## 6 away_team_buildUpPlaySpeedClass 83.44 2.4509804
## 7 away_team_chanceCreationCrossingClass 72.49 1.4705882
## 8 away_team_chanceCreationPassingClass 87.34 0.9803922
## 9 away_team_chanceCreationPositioningClass 35.96 1.4705882
## 10 away_team_chanceCreationShootingClass 65.98 2.4509804
## 11 away_team_defenceAggressionClass 96.98 3.9215686
## 12 away_team_defenceDefenderLineClass 16.91 1.9607843
## 13 away_team_defencePressureClass 9.04 0.4901961
## 14 away_team_defenceTeamWidthClass 6.08 1.4705882
## 15 away_team_home_wins_p 0.00 0.0000000
## 16 away_team_matches 54.18 1.4705882
## 17 away_team_wins 100.00 6.3725490
## 18 home_team_away_wins_p 100.00 9.3137255
## 19 home_team_buildUpPlayDribblingClass 83.53 2.4509804
## 20 home_team_buildUpPlayPassingClass 98.40 5.3921569
## 21 home_team_buildUpPlayPositioningClass 100.00 2.4509804
## 22 home_team_buildUpPlaySpeedClass 79.60 3.4313725
## 23 home_team_chanceCreationCrossingClass 89.85 2.9411765
## 24 home_team_chanceCreationPassingClass 88.77 1.9607843
## 25 home_team_chanceCreationPositioningClass 20.12 0.9803922
## 26 home_team_chanceCreationShootingClass 94.13 4.9019608
## 27 home_team_defenceAggressionClass 5.96 0.4901961
## 28 home_team_defenceDefenderLineClass 6.51 0.9803922
## 29 home_team_defencePressureClass 6.62 0.9803922
## 30 home_team_defenceTeamWidthClass 4.00 0.4901961
## 31 home_team_home_wins_p 0.00 0.0000000
## 32 home_team_matches 94.97 4.9019608
## 33 home_team_wins 100.00 7.8431373
## 34 league_id 0.00 0.0000000
## 35 stage 95.89 9.3137255
Fem servir la informació de les dos mètriques per a generar un conjunt dels atributs més rellevants:
usage_threshold <- 10
splits_threshold <- 6
most_imp <- df_imp$attribute[
df_imp$usage > usage_threshold |
df_imp$splits > splits_threshold
]
test_imp_X <- test_X[most_imp]
train_imp_X <- train_X[most_imp]
Ara procedim a entrenar i mostrar les regles del model:
c50_model_imp <- C5.0(train_imp_X, train_y, rules = TRUE, trials = 100)
summary(c50_model_imp)
##
## Call:
## C5.0.default(x = train_imp_X, y = train_y, trials = 100, rules = TRUE)
##
##
## C5.0 [Release 2.07 GPL Edition] Tue Jan 24 21:44:37 2023
## -------------------------------
##
## Class specified by attribute `outcome'
##
## Read 17086 cases (27 attributes) from undefined.data
##
## ----- Trial 0: -----
##
## Rules:
##
## Rule 0/1: (416/62, lift 1.6)
## away_team_away_wins_p > 0.6447368
## -> class FALSE [0.849]
##
## Rule 0/2: (13/2, lift 1.5)
## home_team_away_wins_p > 0.368421
## home_team_buildUpPlayPassingClass = Long
## -> class FALSE [0.800]
##
## Rule 0/3: (53/12, lift 1.4)
## away_team_buildUpPlayDribblingClass = Little
## away_team_buildUpPlaySpeedClass = Balanced
## home_team_chanceCreationShootingClass = Normal
## home_team_wins <= 33
## stage = 2013/2014
## -> class FALSE [0.764]
##
## Rule 0/4: (5368/1603, lift 1.3)
## away_team_wins > 41
## home_team_away_wins_p <= 0.5384616
## -> class FALSE [0.701]
##
## Rule 0/5: (13486/5452, lift 1.1)
## home_team_away_wins_p <= 0.368421
## -> class FALSE [0.596]
##
## Rule 0/6: (10, lift 2.0)
## away_team_away_wins_p > 0.1555556
## away_team_away_wins_p <= 0.2315789
## away_team_buildUpPlaySpeedClass = Balanced
## away_team_chanceCreationCrossingClass = Lots
## home_team_chanceCreationShootingClass = Normal
## stage = 2013/2014
## -> class TRUE [0.917]
##
## Rule 0/7: (8, lift 2.0)
## away_team_away_wins_p <= 0.2831858
## away_team_wins > 36
## home_team_chanceCreationCrossingClass = Lots
## home_team_chanceCreationShootingClass = Normal
## stage = 2013/2014
## -> class TRUE [0.900]
##
## Rule 0/8: (38/8, lift 1.7)
## away_team_away_wins_p <= 0.3464052
## home_team_away_wins_p <= 0.368421
## home_team_chanceCreationPassingClass in {Risky, Safe}
## home_team_wins > 41
## stage = 2008/2009
## -> class TRUE [0.775]
##
## Rule 0/9: (33/8, lift 1.6)
## away_team_away_wins_p <= 0.3464052
## away_team_buildUpPlayPositioningClass = Free Form
## home_team_chanceCreationShootingClass = Little
## -> class TRUE [0.743]
##
## Rule 0/10: (74/19, lift 1.6)
## away_team_away_wins_p <= 0.5367647
## home_team_away_wins_p > 0.368421
## home_team_buildUpPlaySpeedClass = Fast
## stage = 2013/2014
## -> class TRUE [0.737]
##
## Rule 0/11: (372/101, lift 1.6)
## away_team_away_wins_p <= 0.5367647
## home_team_away_wins_p > 0.368421
## home_team_chanceCreationPassingClass = Normal
## stage = 2009/2010
## -> class TRUE [0.727]
##
## Rule 0/12: (58/17, lift 1.5)
## away_team_away_wins_p <= 0.2315789
## away_team_buildUpPlaySpeedClass = Slow
## -> class TRUE [0.700]
##
## Rule 0/13: (492/149, lift 1.5)
## away_team_away_wins_p <= 0.2315789
## away_team_buildUpPlaySpeedClass = Balanced
## home_team_chanceCreationPositioningClass = Free Form
## home_team_chanceCreationShootingClass = Normal
## -> class TRUE [0.696]
##
## Rule 0/14: (861/268, lift 1.5)
## away_team_away_wins_p <= 0.5367647
## home_team_away_wins_p > 0.368421
## stage in {2010/2011, 2011/2012}
## -> class TRUE [0.688]
##
## Rule 0/15: (3081/998, lift 1.5)
## away_team_away_wins_p <= 0.2315789
## home_team_wins > 33
## -> class TRUE [0.676]
##
## Rule 0/16: (1653/546, lift 1.5)
## away_team_away_wins_p <= 0.3464052
## home_team_wins > 41
## stage in {2010/2011, 2013/2014, 2014/2015}
## -> class TRUE [0.669]
##
## Rule 0/17: (3600/1192, lift 1.5)
## home_team_away_wins_p > 0.368421
## -> class TRUE [0.669]
##
## Rule 0/18: (402/133, lift 1.5)
## away_team_api_id <= 35
## away_team_away_wins_p <= 0.2315789
## away_team_chanceCreationCrossingClass = Normal
## home_team_chanceCreationShootingClass = Lots
## -> class TRUE [0.668]
##
## Rule 0/19: (501/169, lift 1.4)
## away_team_away_wins_p <= 0.2315789
## away_team_buildUpPlayPassingClass = Mixed
## away_team_buildUpPlaySpeedClass = Balanced
## home_team_chanceCreationShootingClass = Little
## -> class TRUE [0.662]
##
## Rule 0/20: (1619/582, lift 1.4)
## away_team_away_wins_p <= 0.1555556
## -> class TRUE [0.640]
##
## Rule 0/21: (336/122, lift 1.4)
## away_team_away_wins_p > 0.1555556
## away_team_away_wins_p <= 0.2315789
## away_team_buildUpPlaySpeedClass = Balanced
## home_team_chanceCreationPositioningClass = Organised
## home_team_chanceCreationShootingClass = Normal
## home_team_matches > 180
## stage = 2008/2009
## -> class TRUE [0.636]
##
## Rule 0/22: (201/77, lift 1.3)
## away_team_away_wins_p <= 0.2315789
## away_team_defenceDefenderLineClass = Cover
## away_team_matches > 218
## home_team_matches > 180
## stage = 2015/2016
## -> class TRUE [0.616]
##
## Rule 0/23: (284/109, lift 1.3)
## away_team_away_wins_p <= 0.3464052
## home_team_chanceCreationShootingClass = Little
## stage in {2009/2010, 2012/2013}
## -> class TRUE [0.615]
##
## Rule 0/24: (97/38, lift 1.3)
## away_team_away_wins_p <= 0.2315789
## away_team_buildUpPlaySpeedClass = Fast
## away_team_defenceAggressionClass = Press
## home_team_chanceCreationPassingClass = Risky
## -> class TRUE [0.606]
##
## Default class: FALSE
##
## ----- Trial 1: -----
##
## Rules:
##
## Rule 1/1: (893.2/189.3, lift 1.5)
## away_team_away_wins_p > 0.5367647
## -> class FALSE [0.787]
##
## Rule 1/2: (45.6/10, lift 1.5)
## away_team_buildUpPlayDribblingClass in {Little, Normal}
## away_team_chanceCreationCrossingClass in {Little, Lots}
## home_team_away_wins_p <= 0.3358209
## home_team_chanceCreationCrossingClass = Normal
## home_team_matches > 210
## stage = 2014/2015
## -> class FALSE [0.769]
##
## Rule 1/3: (106.9/27.2, lift 1.4)
## away_team_buildUpPlaySpeedClass = Fast
## home_team_away_wins_p <= 0.3358209
## home_team_buildUpPlayPassingClass = Mixed
## home_team_chanceCreationCrossingClass = Normal
## stage = 2012/2013
## -> class FALSE [0.741]
##
## Rule 1/4: (1069.3/282.7, lift 1.4)
## away_team_matches > 238
## home_team_wins <= 9
## -> class FALSE [0.735]
##
## Rule 1/5: (2857/831.1, lift 1.4)
## away_team_away_wins_p > 0.3636364
## home_team_away_wins_p <= 0.3486842
## -> class FALSE [0.709]
##
## Rule 1/6: (152.5/50.2, lift 1.3)
## home_team_away_wins_p <= 0.3358209
## home_team_buildUpPlayDribblingClass = Little
## home_team_chanceCreationCrossingClass = Normal
## stage = 2011/2012
## -> class FALSE [0.668]
##
## Rule 1/7: (1666.6/559.4, lift 1.3)
## away_team_buildUpPlayDribblingClass in {Lots, Normal}
## home_team_wins <= 9
## -> class FALSE [0.664]
##
## Rule 1/8: (787.6/300.3, lift 1.2)
## home_team_away_wins_p <= 0.3358209
## home_team_buildUpPlayPassingClass in {Long, Short}
## -> class FALSE [0.618]
##
## Rule 1/9: (473.8/182.1, lift 1.2)
## away_team_buildUpPlayDribblingClass = Lots
## home_team_away_wins_p <= 0.3358209
## -> class FALSE [0.615]
##
## Rule 1/10: (5015.5/1957.7, lift 1.2)
## home_team_away_wins_p <= 0.3358209
## home_team_buildUpPlaySpeedClass in {Balanced, Fast}
## home_team_matches <= 210
## -> class FALSE [0.610]
##
## Rule 1/11: (1239.5/508.1, lift 1.1)
## away_team_away_wins_p > 0.1666667
## away_team_buildUpPlayPassingClass in {Long, Mixed}
## home_team_away_wins_p <= 0.3358209
## stage = 2009/2010
## -> class FALSE [0.590]
##
## Rule 1/12: (2736.6/1144.7, lift 1.1)
## home_team_away_wins_p <= 0.3358209
## home_team_chanceCreationCrossingClass = Normal
## stage in {2013/2014, 2015/2016}
## -> class FALSE [0.582]
##
## Rule 1/13: (1054.5/445.8, lift 1.1)
## away_team_buildUpPlayDribblingClass in {Little, Normal}
## away_team_chanceCreationPositioningClass = Organised
## home_team_away_wins_p <= 0.3358209
## home_team_chanceCreationCrossingClass = Normal
## home_team_chanceCreationPassingClass in {Normal, Safe}
## stage = 2008/2009
## -> class FALSE [0.577]
##
## Rule 1/14: (114.6/41.3, lift 1.3)
## away_team_buildUpPlayDribblingClass = Little
## away_team_matches <= 238
## home_team_buildUpPlaySpeedClass = Balanced
## home_team_wins <= 9
## -> class TRUE [0.637]
##
## Rule 1/15: (3838.7/1465.5, lift 1.3)
## away_team_away_wins_p <= 0.5367647
## home_team_away_wins_p > 0.3486842
## -> class TRUE [0.618]
##
## Rule 1/16: (13485.4/6482.5, lift 1.1)
## away_team_away_wins_p <= 0.3636364
## -> class TRUE [0.519]
##
## Default class: FALSE
##
## ----- Trial 2: -----
##
## Rules:
##
## Rule 2/1: (972.6/281.3, lift 1.4)
## away_team_wins > 29
## home_team_wins <= 9
## -> class FALSE [0.710]
##
## Rule 2/2: (232.2/80.3, lift 1.3)
## home_team_buildUpPlaySpeedClass in {Fast, Slow}
## home_team_wins <= 9
## -> class FALSE [0.653]
##
## Rule 2/3: (6898.1/2758.3, lift 1.2)
## away_team_away_wins_p > 0.2720588
## home_team_away_wins_p <= 0.493421
## -> class FALSE [0.600]
##
## Rule 2/4: (511.3/210.8, lift 1.1)
## home_team_away_wins_p <= 0.4055944
## home_team_buildUpPlayDribblingClass = Normal
## home_team_chanceCreationShootingClass = Lots
## -> class FALSE [0.587]
##
## Rule 2/5: (485.6/208.6, lift 1.1)
## away_team_chanceCreationPassingClass = Safe
## home_team_away_wins_p <= 0.493421
## -> class FALSE [0.570]
##
## Rule 2/6: (5071.7/2198.9, lift 1.1)
## away_team_away_wins_p > 0.1832061
## home_team_away_wins_p <= 0.4055944
## home_team_buildUpPlayDribblingClass = Normal
## stage in {2010/2011, 2011/2012, 2012/2013, 2013/2014}
## -> class FALSE [0.566]
##
## Rule 2/7: (1248.2/402.8, lift 1.4)
## home_team_away_wins_p > 0.493421
## -> class TRUE [0.677]
##
## Rule 2/8: (1127.5/469.9, lift 1.2)
## away_team_away_wins_p <= 0.3464052
## home_team_chanceCreationCrossingClass in {Little, Normal}
## home_team_chanceCreationShootingClass = Normal
## home_team_wins > 9
## stage = 2009/2010
## -> class TRUE [0.583]
##
## Rule 2/9: (3252/1487.5, lift 1.1)
## away_team_away_wins_p <= 0.3464052
## home_team_buildUpPlayDribblingClass = Normal
## home_team_chanceCreationShootingClass = Normal
## home_team_wins > 9
## stage in {2008/2009, 2014/2015, 2015/2016}
## -> class TRUE [0.543]
##
## Rule 2/10: (13007.1/6222.2, lift 1.1)
## away_team_away_wins_p <= 0.3464052
## -> class TRUE [0.522]
##
## Default class: FALSE
##
## ----- Trial 3: -----
##
## Rules:
##
## Rule 3/1: (1435.7/431.6, lift 1.4)
## away_team_away_wins_p > 0.4666667
## -> class FALSE [0.699]
##
## Rule 3/2: (533.6/219.6, lift 1.1)
## home_team_buildUpPlayPassingClass = Long
## -> class FALSE [0.588]
##
## Rule 3/3: (16231/7687.2, lift 1.0)
## home_team_wins <= 75
## -> class FALSE [0.526]
##
## Rule 3/4: (34/9.1, lift 1.5)
## away_team_defenceAggressionClass = Contain
## home_team_matches > 150
## stage = 2015/2016
## -> class TRUE [0.720]
##
## Rule 3/5: (799.1/244.1, lift 1.4)
## away_team_away_wins_p <= 0.4666667
## home_team_wins > 75
## -> class TRUE [0.694]
##
## Rule 3/6: (1754.1/663.3, lift 1.3)
## away_team_wins <= 41
## home_team_away_wins_p > 0.4144737
## -> class TRUE [0.622]
##
## Rule 3/7: (240.2/97.8, lift 1.2)
## away_team_buildUpPlaySpeedClass = Slow
## away_team_wins <= 41
## home_team_matches > 150
## -> class TRUE [0.592]
##
## Rule 3/8: (1171.5/486, lift 1.2)
## away_team_wins <= 41
## home_team_matches > 150
## stage = 2013/2014
## -> class TRUE [0.585]
##
## Rule 3/9: (1122.4/513.1, lift 1.1)
## away_team_buildUpPlaySpeedClass = Balanced
## away_team_wins <= 41
## home_team_buildUpPlaySpeedClass in {Balanced, Fast}
## home_team_matches > 150
## stage = 2009/2010
## -> class TRUE [0.543]
##
## Rule 3/10: (1058.6/490.3, lift 1.1)
## away_team_chanceCreationCrossingClass = Normal
## away_team_wins <= 41
## home_team_buildUpPlayPassingClass = Mixed
## home_team_matches > 150
## stage = 2010/2011
## -> class TRUE [0.537]
##
## Rule 3/11: (3380.6/1588.1, lift 1.1)
## away_team_buildUpPlaySpeedClass = Balanced
## away_team_wins <= 41
## home_team_matches > 150
## stage in {2008/2009, 2011/2012, 2012/2013}
## -> class TRUE [0.530]
##
## Default class: FALSE
##
## ----- Trial 4: -----
##
## Rules:
##
## Rule 4/1: (1405.6/458.8, lift 1.3)
## away_team_away_wins_p > 0.4666667
## -> class FALSE [0.673]
##
## Rule 4/2: (213.5/90.3, lift 1.1)
## away_team_defenceAggressionClass = Double
## -> class FALSE [0.576]
##
## Rule 4/3: (1474.3/657.4, lift 1.1)
## away_team_buildUpPlayPassingClass in {Mixed, Short}
## away_team_defenceAggressionClass = Press
## home_team_wins <= 46
## stage = 2009/2010
## -> class FALSE [0.554]
##
## Rule 4/4: (11430.6/5287.7, lift 1.0)
## home_team_chanceCreationCrossingClass = Normal
## home_team_wins <= 46
## -> class FALSE [0.537]
##
## Rule 4/5: (72.5/23.5, lift 1.4)
## away_team_buildUpPlayPassingClass = Long
## away_team_defenceAggressionClass = Press
## stage = 2009/2010
## -> class TRUE [0.671]
##
## Rule 4/6: (159.7/64.1, lift 1.2)
## away_team_away_wins_p <= 0.4666667
## away_team_chanceCreationShootingClass = Lots
## stage = 2008/2009
## -> class TRUE [0.597]
##
## Rule 4/7: (1728.8/807, lift 1.1)
## away_team_wins <= 59
## stage = 2013/2014
## -> class TRUE [0.533]
##
## Rule 4/8: (1682.9/798.7, lift 1.1)
## away_team_away_wins_p <= 0.4666667
## away_team_buildUpPlaySpeedClass = Balanced
## home_team_away_wins_p > 0.1666667
## stage = 2015/2016
## -> class TRUE [0.525]
##
## Rule 4/9: (15680.4/7824, lift 1.0)
## away_team_away_wins_p <= 0.4666667
## -> class TRUE [0.501]
##
## Default class: FALSE
##
## ----- Trial 5: -----
##
## Rules:
##
## Rule 5/1: (16574.5/8007.9, lift 1.0)
## home_team_away_wins_p <= 0.5735294
## -> class FALSE [0.517]
##
## Rule 5/2: (511.5/158.6, lift 1.4)
## home_team_away_wins_p > 0.5735294
## -> class TRUE [0.689]
##
## Rule 5/3: (90/32.2, lift 1.3)
## away_team_buildUpPlayDribblingClass = Little
## away_team_chanceCreationCrossingClass = Normal
## away_team_wins > 10
## home_team_chanceCreationShootingClass = Little
## -> class TRUE [0.640]
##
## Rule 5/4: (630/271, lift 1.2)
## away_team_defenceDefenderLineClass = Cover
## away_team_wins <= 26
## home_team_matches <= 111
## -> class TRUE [0.570]
##
## Rule 5/5: (2250.9/1017.4, lift 1.1)
## away_team_wins <= 10
## -> class TRUE [0.548]
##
## Rule 5/6: (1180/543.2, lift 1.1)
## away_team_away_wins_p <= 0.5073529
## away_team_buildUpPlayDribblingClass = Normal
## away_team_buildUpPlayPositioningClass = Organised
## away_team_chanceCreationCrossingClass = Normal
## away_team_wins <= 69
## home_team_chanceCreationShootingClass = Normal
## home_team_matches > 111
## stage = 2014/2015
## -> class TRUE [0.540]
##
## Rule 5/7: (1371.3/637.6, lift 1.1)
## away_team_away_wins_p <= 0.5073529
## away_team_wins <= 69
## home_team_chanceCreationCrossingClass in {Little, Normal}
## home_team_chanceCreationShootingClass = Normal
## home_team_matches > 111
## stage = 2009/2010
## -> class TRUE [0.535]
##
## Rule 5/8: (2918.8/1403, lift 1.1)
## away_team_away_wins_p <= 0.5073529
## away_team_wins <= 69
## home_team_chanceCreationShootingClass = Normal
## home_team_matches > 111
## stage in {2008/2009, 2011/2012}
## -> class TRUE [0.519]
##
## Default class: FALSE
##
## ----- Trial 6: -----
##
## Rules:
##
## Rule 6/1: (16266.4/7869.7, lift 1.0)
## home_team_wins <= 75
## -> class FALSE [0.516]
##
## Rule 6/2: (819.6/298.5, lift 1.3)
## home_team_wins > 75
## -> class TRUE [0.635]
##
## Rule 6/3: (516.9/228.2, lift 1.1)
## away_team_matches <= 210
## home_team_matches <= 111
## -> class TRUE [0.558]
##
## Rule 6/4: (902.9/413.8, lift 1.1)
## away_team_away_wins_p <= 0.4078947
## away_team_buildUpPlayDribblingClass = Normal
## away_team_buildUpPlaySpeedClass in {Fast, Slow}
## home_team_matches > 111
## -> class TRUE [0.542]
##
## Rule 6/5: (3251.7/1532.3, lift 1.1)
## away_team_away_wins_p <= 0.4078947
## away_team_buildUpPlayDribblingClass = Normal
## away_team_chanceCreationCrossingClass = Normal
## home_team_buildUpPlayPassingClass = Mixed
## home_team_chanceCreationShootingClass = Normal
## home_team_matches > 111
## stage in {2010/2011, 2013/2014, 2014/2015}
## -> class TRUE [0.529]
##
## Rule 6/6: (1480.3/697.9, lift 1.1)
## home_team_chanceCreationShootingClass = Little
## -> class TRUE [0.528]
##
## Default class: FALSE
##
## ----- Trial 7: -----
##
## Rules:
##
## Rule 7/1: (1423.6/536.4, lift 1.2)
## away_team_away_wins_p > 0.4528302
## -> class FALSE [0.623]
##
## Rule 7/2: (1538.8/675.2, lift 1.1)
## home_team_matches <= 111
## -> class FALSE [0.561]
##
## Rule 7/3: (14262/6966, lift 1.0)
## away_team_away_wins_p <= 0.4528302
## home_team_matches > 111
## -> class TRUE [0.512]
##
## Default class: FALSE
##
## ----- Trial 8: -----
##
## Rules:
##
## Rule 8/1: (16593.8/8001.3, lift 1.0)
## home_team_away_wins_p <= 0.5735294
## -> class FALSE [0.518]
##
## Rule 8/2: (492.2/170.2, lift 1.3)
## home_team_away_wins_p > 0.5735294
## -> class TRUE [0.654]
##
## Rule 8/3: (490.6/229.6, lift 1.1)
## away_team_defenceAggressionClass = Contain
## -> class TRUE [0.532]
##
## Rule 8/4: (5007.2/2354.4, lift 1.1)
## away_team_away_wins_p <= 0.5073529
## away_team_defenceAggressionClass = Press
## home_team_away_wins_p > 0.3145161
## -> class TRUE [0.530]
##
## Default class: FALSE
##
## ----- Trial 9: -----
##
## Rules:
##
## Rule 9/1: (843.4/297.3, lift 1.3)
## away_team_away_wins_p > 0.5073529
## home_team_away_wins_p <= 0.5735294
## -> class FALSE [0.647]
##
## Rule 9/2: (602.3/215.1, lift 1.3)
## away_team_away_wins_p > 0.2735849
## home_team_matches <= 110
## -> class FALSE [0.642]
##
## Rule 9/3: (6239/2835.1, lift 1.1)
## away_team_away_wins_p > 0.2735849
## home_team_away_wins_p <= 0.5735294
## home_team_chanceCreationCrossingClass = Normal
## -> class FALSE [0.546]
##
## Rule 9/4: (487.1/174.9, lift 1.3)
## home_team_away_wins_p > 0.5735294
## -> class TRUE [0.640]
##
## Rule 9/5: (123.8/47.3, lift 1.3)
## away_team_away_wins_p > 0.2735849
## away_team_chanceCreationPassingClass = Risky
## stage = 2008/2009
## -> class TRUE [0.616]
##
## Rule 9/6: (16199.3/8099, lift 1.0)
## away_team_away_wins_p <= 0.5073529
## -> class TRUE [0.500]
##
## Default class: FALSE
##
## ----- Trial 10: -----
##
## Rules:
##
## Rule 10/1: (16603.8/8036.2, lift 1.0)
## home_team_away_wins_p <= 0.5735294
## -> class FALSE [0.516]
##
## Rule 10/2: (482.2/179, lift 1.3)
## home_team_away_wins_p > 0.5735294
## -> class TRUE [0.628]
##
## Default class: FALSE
##
## ----- Trial 11: -----
##
## Rules:
##
## Rule 11/1: (644.1/235.5, lift 1.3)
## away_team_wins > 76
## -> class FALSE [0.634]
##
## Rule 11/2: (10575.3/5122.2, lift 1.0)
## away_team_chanceCreationCrossingClass in {Little, Normal}
## away_team_wins > 23
## home_team_away_wins_p <= 0.6447368
## -> class FALSE [0.516]
##
## Rule 11/3: (15893.4/7803.5, lift 1.0)
## home_team_wins <= 69
## -> class FALSE [0.509]
##
## Rule 11/4: (45.8/14.7, lift 1.4)
## away_team_buildUpPlayPositioningClass = Free Form
## home_team_buildUpPlayDribblingClass = Normal
## home_team_chanceCreationShootingClass = Little
## -> class TRUE [0.673]
##
## Rule 11/5: (151.1/58.5, lift 1.2)
## home_team_buildUpPlayPassingClass = Short
## home_team_chanceCreationShootingClass = Little
## -> class TRUE [0.611]
##
## Rule 11/6: (114.2/46, lift 1.2)
## home_team_buildUpPlayPassingClass = Long
## home_team_chanceCreationShootingClass = Little
## -> class TRUE [0.596]
##
## Rule 11/7: (1428.2/663, lift 1.1)
## away_team_api_id > 4
## away_team_chanceCreationShootingClass = Normal
## away_team_wins <= 76
## home_team_buildUpPlayPassingClass = Mixed
## stage = 2009/2010
## -> class TRUE [0.536]
##
## Rule 11/8: (12377.1/6068.3, lift 1.0)
## away_team_api_id > 4
## away_team_wins <= 76
## home_team_buildUpPlayPassingClass = Mixed
## home_team_chanceCreationShootingClass in {Lots, Normal}
## -> class TRUE [0.510]
##
## Rule 11/9: (16441.9/8182.1, lift 1.0)
## away_team_wins <= 76
## -> class TRUE [0.502]
##
## Default class: FALSE
##
## ----- Trial 12: -----
##
## Rules:
##
## Rule 12/1: (638.2/239.9, lift 1.2)
## away_team_wins > 76
## -> class FALSE [0.624]
##
## Rule 12/2: (1665.9/789.1, lift 1.1)
## away_team_chanceCreationCrossingClass in {Little, Lots}
## home_team_wins <= 69
## -> class FALSE [0.526]
##
## Rule 12/3: (16447.8/8145.3, lift 1.0)
## away_team_wins <= 76
## -> class TRUE [0.505]
##
## Default class: FALSE
##
## ----- Trial 13: -----
##
## Rules:
##
## Rule 13/1: (95.1/29.7, lift 1.4)
## away_team_buildUpPlayDribblingClass in {Little, Lots}
## home_team_buildUpPlayPassingClass = Mixed
## home_team_chanceCreationPassingClass = Safe
## -> class FALSE [0.684]
##
## Rule 13/2: (862.7/335.2, lift 1.2)
## away_team_away_wins_p > 0.5073529
## -> class FALSE [0.611]
##
## Rule 13/3: (526.7/243, lift 1.1)
## home_team_buildUpPlayPassingClass = Long
## -> class FALSE [0.538]
##
## Rule 13/4: (13035.8/6369.5, lift 1.0)
## away_team_buildUpPlayPassingClass = Mixed
## home_team_away_wins_p <= 0.5241935
## home_team_chanceCreationPassingClass = Normal
## -> class FALSE [0.511]
##
## Rule 13/5: (294.8/131.8, lift 1.1)
## away_team_buildUpPlaySpeedClass = Slow
## home_team_buildUpPlayDribblingClass = Normal
## -> class TRUE [0.552]
##
## Rule 13/6: (16223.3/8102.8, lift 1.0)
## away_team_away_wins_p <= 0.5073529
## -> class TRUE [0.501]
##
## Default class: FALSE
##
## ----- Trial 14: -----
##
## Rules:
##
## Rule 14/1: (138.4/45.6, lift 1.3)
## home_team_away_wins_p <= 0.1666667
## stage = 2015/2016
## -> class FALSE [0.668]
##
## Rule 14/2: (558.2/212.4, lift 1.2)
## away_team_wins > 77
## -> class FALSE [0.619]
##
## Rule 14/3: (411.2/179.3, lift 1.1)
## home_team_buildUpPlayPassingClass = Long
## home_team_chanceCreationShootingClass in {Lots, Normal}
## -> class FALSE [0.564]
##
## Rule 14/4: (16527.8/8195.7, lift 1.0)
## away_team_wins <= 77
## -> class TRUE [0.504]
##
## Default class: TRUE
##
## ----- Trial 15: -----
##
## Rules:
##
## Rule 15/1: (16776/8250.9, lift 1.0)
## home_team_away_wins_p <= 0.6447368
## -> class FALSE [0.508]
##
## Rule 15/2: (310/116.1, lift 1.3)
## home_team_away_wins_p > 0.6447368
## -> class TRUE [0.625]
##
## Default class: FALSE
##
## ----- Trial 16: -----
##
## Rules:
##
## Rule 16/1: (44.4/9.8, lift 1.5)
## away_team_api_id > 32
## home_team_chanceCreationPassingClass = Safe
## -> class FALSE [0.768]
##
## Rule 16/2: (107.1/39.6, lift 1.3)
## away_team_buildUpPlayDribblingClass in {Little, Lots}
## home_team_chanceCreationPassingClass = Safe
## -> class FALSE [0.628]
##
## Rule 16/3: (853.7/341.3, lift 1.2)
## away_team_away_wins_p > 0.5073529
## -> class FALSE [0.600]
##
## Rule 16/4: (16232.3/8043.6, lift 1.0)
## away_team_away_wins_p <= 0.5073529
## -> class TRUE [0.504]
##
## Default class: FALSE
##
## ----- Trial 17: -----
##
## Rules:
##
## Rule 17/1: (16620.2/8159, lift 1.0)
## home_team_away_wins_p <= 0.5735294
## -> class FALSE [0.509]
##
## Rule 17/2: (465.8/188.4, lift 1.2)
## home_team_away_wins_p > 0.5735294
## -> class TRUE [0.595]
##
## Default class: FALSE
##
## ----- Trial 18: -----
##
## Rules:
##
## Rule 18/1: (1285.4/547.5, lift 1.1)
## away_team_away_wins_p > 0.4666667
## -> class FALSE [0.574]
##
## Rule 18/2: (15908.1/7863.2, lift 1.0)
## home_team_wins <= 69
## -> class FALSE [0.506]
##
## Rule 18/3: (1090.8/469.4, lift 1.1)
## away_team_away_wins_p <= 0.4666667
## home_team_wins > 69
## -> class TRUE [0.570]
##
## Default class: FALSE
##
## *** boosting reduced to 18 trials since last classifier is very inaccurate
##
##
## Evaluation on training data (17086 cases):
##
## Trial Rules
## ----- ----------------
## No Errors
##
## 0 24 5914(34.6%)
## 1 16 6281(36.8%)
## 2 10 6597(38.6%)
## 3 11 6481(37.9%)
## 4 9 6854(40.1%)
## 5 8 7151(41.9%)
## 6 6 7258(42.5%)
## 7 3 7687(45.0%)
## 8 4 6466(37.8%)
## 9 6 6916(40.5%)
## 10 2 7471(43.7%)
## 11 9 6981(40.9%)
## 12 3 8438(49.4%)
## 13 6 7197(42.1%)
## 14 4 8456(49.5%)
## 15 2 7589(44.4%)
## 16 4 8457(49.5%)
## 17 2 7471(43.7%)
## boost 5903(34.5%) <<
##
##
## (a) (b) <-classified as
## ---- ----
## 6950 2276 (a): class FALSE
## 3627 4233 (b): class TRUE
##
##
## Attribute usage:
##
## 100.00% away_team_away_wins_p
## 100.00% away_team_wins
## 100.00% home_team_away_wins_p
## 100.00% home_team_wins
## 93.56% home_team_matches
## 90.97% home_team_chanceCreationShootingClass
## 90.74% stage
## 80.81% home_team_buildUpPlayPassingClass
## 78.94% away_team_chanceCreationCrossingClass
## 78.15% home_team_chanceCreationPassingClass
## 77.88% away_team_buildUpPlayPassingClass
## 76.64% home_team_chanceCreationCrossingClass
## 72.75% away_team_api_id
## 51.73% home_team_buildUpPlayDribblingClass
## 44.38% away_team_buildUpPlaySpeedClass
## 42.28% away_team_defenceAggressionClass
## 42.05% away_team_buildUpPlayDribblingClass
## 35.81% home_team_buildUpPlaySpeedClass
## 10.69% away_team_matches
## 9.21% away_team_chanceCreationShootingClass
## 6.94% away_team_buildUpPlayPositioningClass
## 6.12% away_team_chanceCreationPositioningClass
## 4.85% home_team_chanceCreationPositioningClass
## 4.62% away_team_defenceDefenderLineClass
## 3.54% away_team_chanceCreationPassingClass
##
##
## Time: 2.3 secs
Seguim obtenint moltes regles, per tant, el que hem de fer ara és
podar l’arbre. La llibreria C50 ens permet especificar si
volem fer servir mètodes de poda, com per exemple winnow,
que fa una selecció dels atributs i noGlobalPruning, que
simplifica l’arbre:
c50_model_prune <- C5.0(train_imp_X, train_y, rules = TRUE,
control = C5.0Control(winnow = TRUE))
summary(c50_model_prune)
##
## Call:
## C5.0.default(x = train_imp_X, y = train_y, rules = TRUE, control
## = C5.0Control(winnow = TRUE))
##
##
## C5.0 [Release 2.07 GPL Edition] Tue Jan 24 21:44:40 2023
## -------------------------------
##
## Class specified by attribute `outcome'
##
## Read 17086 cases (27 attributes) from undefined.data
##
## 16 attributes winnowed
## Estimated importance of remaining attributes:
##
## 6% away_team_away_wins_p
## 2% home_team_away_wins_p
## 2% home_team_wins
## <1% home_team_buildUpPlaySpeedClass
## <1% away_team_buildUpPlaySpeedClass
## <1% home_team_buildUpPlayPassingClass
## <1% away_team_chanceCreationCrossingClass
## <1% home_team_buildUpPlayDribblingClass
## <1% away_team_buildUpPlayPositioningClass
## <1% away_team_chanceCreationPositioningClass
##
## Rules:
##
## Rule 1: (953/156, lift 1.5)
## away_team_away_wins_p > 0.5367647
## -> class FALSE [0.836]
##
## Rule 2: (13/2, lift 1.5)
## home_team_away_wins_p > 0.368421
## home_team_buildUpPlayPassingClass = Long
## -> class FALSE [0.800]
##
## Rule 3: (8436/2837, lift 1.2)
## away_team_away_wins_p > 0.2315789
## home_team_away_wins_p <= 0.368421
## -> class FALSE [0.664]
##
## Rule 4: (8267/2905, lift 1.2)
## away_team_away_wins_p > 0.1555556
## home_team_away_wins_p <= 0.368421
## home_team_wins <= 33
## -> class FALSE [0.649]
##
## Rule 5: (3455/1084, lift 1.5)
## away_team_away_wins_p <= 0.5367647
## home_team_away_wins_p > 0.368421
## home_team_buildUpPlayPassingClass in {Mixed, Short}
## -> class TRUE [0.686]
##
## Rule 6: (3081/998, lift 1.5)
## away_team_away_wins_p <= 0.2315789
## home_team_wins > 33
## -> class TRUE [0.676]
##
## Rule 7: (1619/582, lift 1.4)
## away_team_away_wins_p <= 0.1555556
## -> class TRUE [0.640]
##
## Default class: FALSE
##
##
## Evaluation on training data (17086 cases):
##
## Rules
## ----------------
## No Errors
##
## 7 6144(36.0%) <<
##
##
## (a) (b) <-classified as
## ---- ----
## 7161 2065 (a): class FALSE
## 4079 3781 (b): class TRUE
##
##
## Attribute usage:
##
## 99.93% away_team_away_wins_p
## 85.23% home_team_away_wins_p
## 66.42% home_team_wins
## 20.30% home_team_buildUpPlayPassingClass
##
##
## Time: 0.5 secs
A continuació fem una breu explicació de les regles extretes:
Com podem veure, el model dona molta importància al rediment passat dels equips, de fet, la variable que més usa és la proporció de victòries fora de casa de l’equip visitant.
I ara el visualitzem:
c50_model_prune <- C5.0(train_imp_X, train_y,
control = C5.0Control(winnow = TRUE))
plot(c50_model_prune, gp = gpar(fontsize = 9.5))
Com podem comprovar, hem obtingut un arbre molt més senzill i més fàcil d’explicar.
En total hem creat tres models diferents basats en els arbres C5.0 de Quinlan. Hem pogut veure la seua representació gràfica i les regles en forma escrita. Però encara no sabem quins models funcionen millor. Per a fer-ho, hem d’estudiar la seua qualitat mostrant la matriu de confusió per a cadascun dels arbres. A més, calculem la seua precisió (precision), la sensibilitat (recall) i la f-measure per tenir unes mètriques que ens ajudin a escollir el millor model.
Carreguem el paquet gmodels per a poder fer servir la
funció CrossTable(...), que calcula la matriu de
confusió:
packages <- c("gmodels")
not_installed <- packages[!(packages %in% installed.packages())]
if (length(not_installed) > 0) {
install.packages(not_installed, repos = "http:/cran.us.r-project.org")
}
lapply(packages, library, character.only = TRUE)
## [[1]]
## [1] "gmodels" "grid" "C50" "dbscan" "fpc"
## [6] "cluster" "corrplot" "factoextra" "xfun" "dplyr"
## [11] "Rmisc" "plyr" "lattice" "ggplot2" "RSQLite"
## [16] "stats" "graphics" "grDevices" "utils" "datasets"
## [21] "methods" "base"
A continuació, carreguem les funcions que ens ajudaran a estudiar la qualitat:
calculate_accuracy <- function(predicted, correct) {
return(sum(predicted == correct) / length(predicted))
}
calculate_precision <- function(predicted, correct) {
cross_table <- gmodels::CrossTable(correct, predicted,
prop.chisq = FALSE, prop.c = FALSE, prop.r = FALSE,
dnn = c("Reality", "Prediction")
)
precision <- cross_table$prop.col[2, 2]
return(precision)
}
calculate_recall <- function(predicted, correct) {
cross_table <- gmodels::CrossTable(correct, predicted,
prop.chisq = FALSE, prop.c = FALSE, prop.r = FALSE,
dnn = c("Reality", "Prediction")
)
recall <- cross_table$prop.row[2, 2]
return(recall)
}
calculate_f_measure <- function(precision, recall) {
f_measure <- (precision * recall) / (precision + recall)
return(f_measure)
}
Creem un data frame on guardarem els resultats per a cada model:
models_names <- c("Basic", "Importance", "Pruned")
df_tree_quality <- data.frame(model = models_names,
accuracy = double(length(models_names)),
precision = double(length(models_names)),
recall = double(length(models_names)),
f_measure = double(length(models_names)))
Ara, procedim a calcular les diferents mètriques per a cada model:
predicted_labels <- predict(c50_model, test_X, type = "class")
df_tree_quality[1, ]$accuracy <- calculate_accuracy(predicted_labels, test_y)
df_tree_quality[1, ]$precision <- calculate_precision(predicted_labels, test_y)
##
##
## Cell Contents
## |-------------------------|
## | N |
## | N / Table Total |
## |-------------------------|
##
##
## Total Observations in Table: 8543
##
##
## | Prediction
## Reality | FALSE | TRUE | Row Total |
## -------------|-----------|-----------|-----------|
## FALSE | 3453 | 1194 | 4647 |
## | 0.404 | 0.140 | |
## -------------|-----------|-----------|-----------|
## TRUE | 1920 | 1976 | 3896 |
## | 0.225 | 0.231 | |
## -------------|-----------|-----------|-----------|
## Column Total | 5373 | 3170 | 8543 |
## -------------|-----------|-----------|-----------|
##
##
df_tree_quality[1, ]$recall <- calculate_recall(predicted_labels, test_y)
##
##
## Cell Contents
## |-------------------------|
## | N |
## | N / Table Total |
## |-------------------------|
##
##
## Total Observations in Table: 8543
##
##
## | Prediction
## Reality | FALSE | TRUE | Row Total |
## -------------|-----------|-----------|-----------|
## FALSE | 3453 | 1194 | 4647 |
## | 0.404 | 0.140 | |
## -------------|-----------|-----------|-----------|
## TRUE | 1920 | 1976 | 3896 |
## | 0.225 | 0.231 | |
## -------------|-----------|-----------|-----------|
## Column Total | 5373 | 3170 | 8543 |
## -------------|-----------|-----------|-----------|
##
##
df_tree_quality[1, ]$f_measure <-
calculate_f_measure(df_tree_quality[1, ]$precision,
df_tree_quality[1, ]$recall)
predicted_labels <- predict(c50_model_imp, test_imp_X, type = "class")
df_tree_quality[2, ]$accuracy <- calculate_accuracy(predicted_labels, test_y)
df_tree_quality[2, ]$precision <- calculate_precision(predicted_labels, test_y)
##
##
## Cell Contents
## |-------------------------|
## | N |
## | N / Table Total |
## |-------------------------|
##
##
## Total Observations in Table: 8543
##
##
## | Prediction
## Reality | FALSE | TRUE | Row Total |
## -------------|-----------|-----------|-----------|
## FALSE | 3455 | 1192 | 4647 |
## | 0.404 | 0.140 | |
## -------------|-----------|-----------|-----------|
## TRUE | 1914 | 1982 | 3896 |
## | 0.224 | 0.232 | |
## -------------|-----------|-----------|-----------|
## Column Total | 5369 | 3174 | 8543 |
## -------------|-----------|-----------|-----------|
##
##
df_tree_quality[2, ]$recall <- calculate_recall(predicted_labels, test_y)
##
##
## Cell Contents
## |-------------------------|
## | N |
## | N / Table Total |
## |-------------------------|
##
##
## Total Observations in Table: 8543
##
##
## | Prediction
## Reality | FALSE | TRUE | Row Total |
## -------------|-----------|-----------|-----------|
## FALSE | 3455 | 1192 | 4647 |
## | 0.404 | 0.140 | |
## -------------|-----------|-----------|-----------|
## TRUE | 1914 | 1982 | 3896 |
## | 0.224 | 0.232 | |
## -------------|-----------|-----------|-----------|
## Column Total | 5369 | 3174 | 8543 |
## -------------|-----------|-----------|-----------|
##
##
df_tree_quality[2, ]$f_measure <-
calculate_f_measure(df_tree_quality[2, ]$precision,
df_tree_quality[2, ]$recall)
predicted_labels <- predict(c50_model_prune, test_imp_X, type = "class")
df_tree_quality[3, ]$accuracy <- calculate_accuracy(predicted_labels, test_y)
df_tree_quality[3, ]$precision <- calculate_precision(predicted_labels, test_y)
##
##
## Cell Contents
## |-------------------------|
## | N |
## | N / Table Total |
## |-------------------------|
##
##
## Total Observations in Table: 8543
##
##
## | Prediction
## Reality | FALSE | TRUE | Row Total |
## -------------|-----------|-----------|-----------|
## FALSE | 3594 | 1053 | 4647 |
## | 0.421 | 0.123 | |
## -------------|-----------|-----------|-----------|
## TRUE | 2097 | 1799 | 3896 |
## | 0.245 | 0.211 | |
## -------------|-----------|-----------|-----------|
## Column Total | 5691 | 2852 | 8543 |
## -------------|-----------|-----------|-----------|
##
##
df_tree_quality[3, ]$recall <- calculate_recall(predicted_labels, test_y)
##
##
## Cell Contents
## |-------------------------|
## | N |
## | N / Table Total |
## |-------------------------|
##
##
## Total Observations in Table: 8543
##
##
## | Prediction
## Reality | FALSE | TRUE | Row Total |
## -------------|-----------|-----------|-----------|
## FALSE | 3594 | 1053 | 4647 |
## | 0.421 | 0.123 | |
## -------------|-----------|-----------|-----------|
## TRUE | 2097 | 1799 | 3896 |
## | 0.245 | 0.211 | |
## -------------|-----------|-----------|-----------|
## Column Total | 5691 | 2852 | 8543 |
## -------------|-----------|-----------|-----------|
##
##
df_tree_quality[3, ]$f_measure <-
calculate_f_measure(df_tree_quality[3, ]$precision,
df_tree_quality[3, ]$recall)
df_tree_quality
## model accuracy precision recall f_measure
## 1 Basic 0.6354910 0.6233438 0.5071869 0.2796490
## 2 Importance 0.6364275 0.6244486 0.5087269 0.2803395
## 3 Pruned 0.6312771 0.6307854 0.4617556 0.2665975
Veiem com els resultats són molt similars entre tots els models. El model que té una f-measure més alta és el que està filtrat amb les variables més importants. També veiem com l’arbre podat té una precisió un pèl més alta que la resta, però amb la pitjor sensibilitat.
Queda clar que el fet de reduir el nombre de regles no afecta negativament a la qualitat del model. Per això, ens quedem amb l’últim, ja que, amb resultats similars (encara que pitjors) és molt més fàcil d’explicar.
Els resultats dels models no són els desitjats, però almenys hem aconseguit descriure en poques regles si l’equip local guanyarà o no.
Els motius pels quals no ha acabat de funcionar són que el resultat d’un partit de futbol és molt complicat d’endevinar. Hi intervenen molts factors i ni les persones més expertes són capaces de fer una predicció fiable.
També és possible que aquest tipus de models basats en arbres de decisió no siguin adequats per a resoldre aquest problema.
Els tres arbres generats s’han basat principalment en els resultats històrics dels equips. Això té molt sentit, ja que, si veiem que s’enfronten equips amb una diferència de qualitat molt gran és fàcil deduir que l’equip més bo guanyarà.
La resta d’informació proporcionada als models tenia en compte l’estil de joc dels equips. Pel que sembla, l’estil de joc no és gaire determinant de cara a predir qui guanyarà.
De cara a futurs anàlisis, seria molt interessant afegir informació dels equips. Per exemple, es podria afegir els gols per partit que marquen, o la valoració mitjana dels jugadors de la plantilla. Això no s’ha fet en aquesta versió perquè aquesta informació no era fàcil de trobar. Per afegir-la, cal fer càlculs i creuar dades entre les taules i malauradament, no s’ha disposat del temps necessari per a fer-ho.
En la secció anterior hem vist com l’arbre de decisió C5.0 de Quinlan no ha donat gaires bons resultats. En aquesta secció aplicarem el model supervisat Random Forest. Aquest es basa a crear molts arbres de decisió diferents i fer-los servir per a prendre decisions en conjunt.
El que volem predir és el mateix que en l’apartat anterior, és a dir, si l’equip local guanyarà el partit. Per a que els models siguin comparables utilitzem les mateixes dades que anteriorment.
Abans de res, carreguem la llibreria randomForest que és
la que ens permetrà entrenar el model:
packages <- c("randomForest")
not_installed <- packages[!(packages %in% installed.packages())]
if (length(not_installed) > 0) {
install.packages(not_installed, repos = "http:/cran.us.r-project.org")
}
lapply(packages, library, character.only = TRUE)
## [[1]]
## [1] "randomForest" "gmodels" "grid" "C50" "dbscan"
## [6] "fpc" "cluster" "corrplot" "factoextra" "xfun"
## [11] "dplyr" "Rmisc" "plyr" "lattice" "ggplot2"
## [16] "RSQLite" "stats" "graphics" "grDevices" "utils"
## [21] "datasets" "methods" "base"
Ara que hem definit la tasca i ja hem carregat els paquets necessaris, ja podem generar el model.
Ho fem amb la funció randomForest(...). Aquesta admet el
paràmetre ntree que serveix per indicar el nombre d’arbres
a crear. Escollim un nombre alt per assegurar-nos que cada fila es
prediu diverses vegades:
rf_model <- randomForest(x = train_X, y = train_y, ntree = 5000)
rf_model
##
## Call:
## randomForest(x = train_X, y = train_y, ntree = 5000)
## Type of random forest: classification
## Number of trees: 5000
## No. of variables tried at each split: 5
##
## OOB estimate of error rate: 37.97%
## Confusion matrix:
## FALSE TRUE class.error
## FALSE 6477 2749 0.2979623
## TRUE 3738 4122 0.4755725
Ara que ja tenim el model creat, podem avaluar-ne la seua qualitat. Fem servir les mateixes mètriques que abans, per tant, no cal carregar cap funció nova:
predicted_labels <- predict(rf_model, test_X, type = "class")
df_tree_quality[4, ]$model <- "Random Forest"
df_tree_quality[4, ]$accuracy <- calculate_accuracy(predicted_labels, test_y)
df_tree_quality[4, ]$precision <- calculate_precision(predicted_labels, test_y)
##
##
## Cell Contents
## |-------------------------|
## | N |
## | N / Table Total |
## |-------------------------|
##
##
## Total Observations in Table: 8543
##
##
## | Prediction
## Reality | FALSE | TRUE | Row Total |
## -------------|-----------|-----------|-----------|
## FALSE | 3286 | 1361 | 4647 |
## | 0.385 | 0.159 | |
## -------------|-----------|-----------|-----------|
## TRUE | 1873 | 2023 | 3896 |
## | 0.219 | 0.237 | |
## -------------|-----------|-----------|-----------|
## Column Total | 5159 | 3384 | 8543 |
## -------------|-----------|-----------|-----------|
##
##
df_tree_quality[4, ]$recall <- calculate_recall(predicted_labels, test_y)
##
##
## Cell Contents
## |-------------------------|
## | N |
## | N / Table Total |
## |-------------------------|
##
##
## Total Observations in Table: 8543
##
##
## | Prediction
## Reality | FALSE | TRUE | Row Total |
## -------------|-----------|-----------|-----------|
## FALSE | 3286 | 1361 | 4647 |
## | 0.385 | 0.159 | |
## -------------|-----------|-----------|-----------|
## TRUE | 1873 | 2023 | 3896 |
## | 0.219 | 0.237 | |
## -------------|-----------|-----------|-----------|
## Column Total | 5159 | 3384 | 8543 |
## -------------|-----------|-----------|-----------|
##
##
df_tree_quality[4, ]$f_measure <-
calculate_f_measure(df_tree_quality[4, ]$precision,
df_tree_quality[4, ]$recall)
df_tree_quality
## model accuracy precision recall f_measure
## 1 Basic 0.6354910 0.6233438 0.5071869 0.2796490
## 2 Importance 0.6364275 0.6244486 0.5087269 0.2803395
## 3 Pruned 0.6312771 0.6307854 0.4617556 0.2665975
## NA Random Forest 0.6214445 0.5978132 0.5192505 0.2778846
Com podem veure, els resultats són molt similars que els obtinguts amb els arbres de decisió C5.0 de Quinlan. Malgrat tot, aquests tenen la sensibilitat una mica més alta que la resta, però, en canvi, perden precisió. Si ens basem en la f-measure podem determinar que és el segon o tercer (depèn de l’execució) millor model dels quatre que hem creat. Això ens deixa en la mateixa situació que en la que estàvem, ja que, no hem assolit un model prou fiable per a predir si el guanyador del partit serà l’equip local.
El que podem concloure d’aquestes anàlisis és que probablement tots els models basats en arbres de decisió aconsegueixin uns resultats similars. Estaria bé provar amb un altre tipus d’algorismes per a saber si funcionen millor o pitjor.
En tot cas, el que és més probable que estigui passant és que les dades són insuficients per a intentar predir això. Hauríem de profunditzar en la recollida de dades i calcular les noves característiques que ja s’han comentat en l’apartat anterior.
Després d’analitzar i treballar amb aquest conjunt de dades és important, de cara a nous treballs, destacar els riscos que comporta utilitzar-lo. A continuació, esmentem alguns dels perills que ens podem trobar:
Un cop acabat aquest projecte considerem important fer alguns apunts sobre aquest. En aquest apartat es comenten les principals limitacions que hem trobat, una explicació sobre com les hem solucionat i, finalment una valoració sobre el coneixement adquirit durant la realització de l’activitat.
Com ja s’ha comentat en l’apartat anterior, durant la realització del treball s’han trobat algunes dificultats inesperades que han complicat el projecte. A continuació esmentem algunes d’elles:
Les solucions que s’han trobat per a les anteriors dificultats han sigut:
Malgrat la poca experiència en solucionar aquest tipus de problemes, crec que en general ens n’hem sortit de forma satisfactòria.
Per acabar, volem comentar que durant aquest projecte sobretot hem après que cal estudiar i entendre molt bé les dades abans de començar a tractar-les. També hem vist que si no es planifiquen correctament els punts a estudiar és molt probable que ens deixem coses durant el pretractament de les dades. Si això passa, el fet de tornar enrere i tractar un altre cop les dades acaba comportant una pèrdua de temps important.
Malgrat que ja teníem una certa experiència realitzant projectes de mineria de dades, mai n’havíem dut a terme un de tan complet. Sempre havien sigut més curts i fàcils. Haver fet aquest exercici des del principi fins al final ens ha millorat la capacitat analítica i ens ha fet donar compte que les dades són vitals per aconseguir un bon resultat.
També hem millorat molt amb l’ús del llenguatge R. Fins ara havíem acomplert petits projectes que ens havien donat una idea sobre com fer-lo anar. Però ara notem que anem molt més ràpids a l’hora de programar.
A més, en el conjunt global de l’assignatura, també valorem molt el fet d’haver realitzat tantes pràctiques diferents, ja que, en cada una tocat temes diferents i ens ha ajudat a aprofundir en elles. Gràcies a elles també hem pogut recopilar una sèrie d’snippets que es podran aplicar en altres projectes.
Finalment, voldríem comentar que aquesta assignatura ens ha semblat útil i completa perquè molt probablement podrem aplicar aquests coneixements en altres projectes laborals i personals.
Aquesta secció conté les pàgines web visitades durant el treball i d’on s’ha agafat idees per a l’elaboració del treball.
A més, durant la realització del treball també s’ha consultat en algunes ocasions ChatGPT per a orientar-nos a fer alguns càlculs. Sempre s’ha fet servir com a eina de suport i s’ha comprovat que el que deia era veritat. De fet, s’ha pogut demostrar, que en certs casos no és capaç de donar un codi vàlid en R.